- struct keyring_upcall_param uparam;
- key_serial_t keyid;
- key_serial_t sring;
- key_serial_t inst_keyring;
- pid_t child;
- struct lgss_mech_type *mech;
- struct lgss_cred *cred;
-
- set_log_level();
-
- logmsg(LL_TRACE, "start parsing parameters\n");
- /*
- * parse & sanity check upcall parameters
- * expected to be called with:
- * [1]: operation
- * [2]: key ID
- * [3]: key type
- * [4]: key description
- * [5]: call out info
- * [6]: UID
- * [7]: GID
- * [8]: thread keyring
- * [9]: process keyring
- * [10]: session keyring
- */
- if (argc != 10 + 1) {
- logmsg(LL_ERR, "invalid parameter number %d\n", argc);
- return 1;
- }
-
- logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
- argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);
-
- memset(&uparam, 0, sizeof(uparam));
-
- if (strcmp(argv[1], "create") != 0) {
- logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
- return 1;
- }
-
- if (sscanf(argv[2], "%d", &keyid) != 1) {
- logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
- return 1;
- }
-
- if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
- logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
- return 1;
- }
-
- if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
- logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
- return 1;
- }
-
- if (sscanf(argv[10], "%d", &sring) != 1) {
- logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
- return 1;
- }
-
- if (parse_callout_info(argv[5], &uparam)) {
- logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
- return 1;
- }
-
- logmsg(LL_TRACE, "parsing parameters OK\n");
-
- /*
- * prepare a cred
- */
- mech = lgss_name2mech(uparam.kup_mech);
- if (mech == NULL) {
- logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
- keyid, uparam.kup_mech);
- return 1;
- }
-
- if (lgss_mech_initialize(mech)) {
- logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
- keyid, mech->lmt_name);
- return 1;
- }
-
- cred = lgss_create_cred(mech);
- if (cred == NULL) {
- logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
- keyid, mech->lmt_name);
- return 1;
- }
-
- cred->lc_uid = uparam.kup_uid;
- cred->lc_fl_root = (uparam.kup_is_root != 0);
- cred->lc_fl_mds = (uparam.kup_is_mds != 0);
- cred->lc_tgt_nid = uparam.kup_nid;
- cred->lc_tgt_svc = uparam.kup_svc;
-
- if (lgss_prepare_cred(cred)) {
- logmsg(LL_ERR, "key %08x: failed to prepare credentials "
- "for user %d\n", keyid, uparam.kup_uid);
- return 1;
- }
-
- /* pre initialize the key. note the keyring linked to is actually of the
- * original requesting process, not _this_ upcall process. if it's for
- * root user, don't link to any keyrings because we want fully control
- * on it, and share it among all root sessions; otherswise link to
- * session keyring.
- */
- if (cred->lc_fl_root || cred->lc_fl_mds)
- inst_keyring = 0;
- else
- inst_keyring = KEY_SPEC_SESSION_KEYRING;
-
- if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
- logmsg(LL_ERR, "instantiate key %08x: %s\n",
- keyid, strerror(errno));
- return 1;
- }
-
- logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
-
- /*
- * fork a child to do the real gss negotiation
- */
- child = fork();
- if (child == -1) {
- logmsg(LL_ERR, "key %08x: can't create child: %s\n",
- keyid, strerror(errno));
- return 1;
- } else if (child == 0) {
- return lgssc_kr_negotiate(keyid, cred, &uparam);
- }
-
- logmsg(LL_TRACE, "forked child %d\n", child);
- return 0;
+ struct keyring_upcall_param uparam;
+ key_serial_t keyid;
+ key_serial_t sring;
+ pid_t child;
+ int req_fd[2] = { -1, -1 };
+ int reply_fd[2] = { -1, -1 };
+ struct lgss_mech_type *mech;
+ struct lgss_cred *cred;
+ char path[PATH_MAX] = "";
+ int other_ns = 0;
+ int rc = 0;
+ struct stat parent_ns = { .st_ino = 0 };
+ struct stat caller_ns = { .st_ino = 0 };
+
+ set_log_level();
+
+ logmsg(LL_TRACE, "start parsing parameters\n");
+ /*
+ * parse & sanity check upcall parameters
+ * expected to be called with:
+ * [1]: operation
+ * [2]: key ID
+ * [3]: key type
+ * [4]: key description
+ * [5]: call out info
+ * [6]: UID
+ * [7]: GID
+ * [8]: thread keyring
+ * [9]: process keyring
+ * [10]: session keyring
+ */
+ if (argc != 10 + 1) {
+ logmsg(LL_ERR, "invalid parameter number %d\n", argc);
+ return 1;
+ }
+
+ logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
+ argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);
+
+ memset(&uparam, 0, sizeof(uparam));
+
+ if (strcmp(argv[1], "create") != 0) {
+ logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
+ return 1;
+ }
+
+ if (sscanf(argv[2], "%d", &keyid) != 1) {
+ logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
+ return 1;
+ }
+
+ if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
+ logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
+ return 1;
+ }
+
+ if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
+ logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
+ return 1;
+ }
+
+ if (sscanf(argv[10], "%d", &sring) != 1) {
+ logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
+ return 1;
+ }
+
+ if (parse_callout_info(argv[5], &uparam)) {
+ logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
+ return 1;
+ }
+
+ logmsg(LL_TRACE, "parsing parameters OK\n");
+
+ /*
+ * prepare a cred
+ */
+ mech = lgss_name2mech(uparam.kup_mech);
+ if (mech == NULL) {
+ logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
+ keyid, uparam.kup_mech);
+ return 1;
+ }
+
+ if (lgss_mech_initialize(mech)) {
+ logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
+ keyid, mech->lmt_name);
+ return 1;
+ }
+
+ cred = lgss_create_cred(mech);
+ if (cred == NULL) {
+ logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
+ keyid, mech->lmt_name);
+ return 1;
+ }
+
+ cred->lc_uid = uparam.kup_uid;
+ cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0;
+ cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0;
+ cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0;
+ cred->lc_tgt_nid = uparam.kup_nid;
+ cred->lc_tgt_svc = uparam.kup_svc;
+ cred->lc_tgt_uuid = uparam.kup_tgt;
+ cred->lc_svc_type = uparam.kup_svc_type;
+ cred->lc_self_nid = uparam.kup_selfnid;
+
+ /* Is caller in different namespace? */
+ /* If passed caller's pid is 0, it means we have to stick
+ * with current namespace.
+ */
+ if (uparam.kup_pid) {
+ snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid());
+ if (stat(path, &parent_ns)) {
+ logmsg(LL_DEBUG, "cannot stat %s: %s\n",
+ path, strerror(errno));
+ } else {
+ snprintf(path, sizeof(path), "/proc/%d/ns/mnt",
+ uparam.kup_pid);
+ if (stat(path, &caller_ns))
+ logmsg(LL_DEBUG, "cannot stat %s: %s\n",
+ path, strerror(errno));
+ else if (caller_ns.st_ino != parent_ns.st_ino)
+ other_ns = 1;
+ }
+ }
+
+ /*
+ * if caller's namespace is different, fork a child and associate it
+ * with caller's namespace to do credentials preparation
+ */
+ if (other_ns) {
+ logmsg(LL_TRACE, "caller's namespace is different\n");
+
+ /* use pipes to pass info between child and parent processes */
+ if (pipe(req_fd) == -1) {
+ logmsg(LL_ERR, "key %08x: pipe failed: %s\n",
+ keyid, strerror(errno));
+ return 1;
+ }
+ if (pipe(reply_fd) == -1) {
+ logmsg(LL_ERR, "key %08x: pipe failed: %s\n",
+ keyid, strerror(errno));
+ return 1;
+ }
+
+ child = fork();
+ if (child == -1) {
+ logmsg(LL_ERR, "key %08x: can't create child: %s\n",
+ keyid, strerror(errno));
+ rc = 1;
+ goto out_pipe;
+ } else if (child == 0) {
+ int rc2;
+ /* child process: carry out credentials preparation
+ * in caller's namespace */
+
+ close(req_fd[0]); /* close unsed read end */
+ req_fd[0] = -1;
+ close(reply_fd[1]); /* close unsed write end */
+ reply_fd[1] = -1;
+
+ if (associate_with_ns(path) != 0) {
+ logmsg(LL_ERR,
+ "failed to attach to pid %d namespace: "
+ "%s\n", uparam.kup_pid, strerror(errno));
+ rc = 1;
+ goto out_pipe;
+ }
+ logmsg(LL_TRACE, "working in namespace of pid %d\n",
+ uparam.kup_pid);
+
+ rc = prepare_and_instantiate(cred, keyid,
+ uparam.kup_uid);
+
+ /* send to parent the status of credentials preparation
+ * and key instantiation */
+ rc2 = send_to(req_fd[1], &rc, sizeof(rc));
+ rc = (rc == 0 ? rc2 : rc);
+ if (rc != 0)
+ goto out_pipe;
+
+ /* now do real gss negotiation
+ * parent main process will not wait for us,
+ * as it has to be done in the background */
+ rc = lgssc_kr_negotiate(keyid, cred, &uparam,
+ req_fd, reply_fd);
+ goto out_pipe;
+ } else {
+ int rc2;
+ /* parent process: exchange info with child carrying out
+ * credentials preparation */
+
+ close(req_fd[1]); /* close unsed write end */
+ req_fd[1] = -1;
+ close(reply_fd[0]); /* close unsed read end */
+ reply_fd[0] = -1;
+
+ /* get status of credentials preparation
+ * and key instantiation */
+ rc2 = receive_from(req_fd[0], &rc, sizeof(rc));
+ if (rc2 != 0 || rc != 0) {
+ logmsg(LL_ERR, "child failed preparing creds: "
+ "%s\n",
+ rc2 != 0 ? strerror(-rc2)
+ : strerror(rc));
+ goto out_pipe;
+ }
+
+ /*
+ * fork a child here to participate in gss negotiation,
+ * as it has to be done in the background
+ */
+ child = fork();
+ if (child == -1) {
+ logmsg(LL_ERR,
+ "key %08x: can't create child: %s\n",
+ keyid, strerror(errno));
+ rc = 1;
+ goto out_pipe;
+ } else if (child == 0) {
+ struct lgssd_ioctl_param param;
+ char outbuf[8192] = { 0 };
+ void *gss_token = NULL;
+
+ /* get ioctl buffer from child */
+ rc = receive_from(req_fd[0], ¶m,
+ sizeof(param));
+ if (rc != 0)
+ goto out_pipe;
+
+ gss_token = calloc(1, param.send_token_size);
+ if (gss_token == NULL)
+ goto out_pipe;
+
+ /* get gss token from child */
+ rc = receive_from(req_fd[0], gss_token,
+ param.send_token_size);
+ if (rc != 0)
+ goto out_token;
+
+ param.send_token = (char *)gss_token;
+ param.reply_buf_size = sizeof(outbuf);
+ param.reply_buf = outbuf;
+
+ /* do ioctl in place of child process carrying
+ * out credentials negotiation: as it runs in
+ * a container, it might not be able to
+ * perform ioctl */
+ rc = gss_do_ioctl(¶m);
+ if (rc != 0)
+ goto out_token;
+
+ /* send ioctl status to child */
+ rc = send_to(reply_fd[1], ¶m.status,
+ sizeof(param.status));
+ if (rc != 0)
+ goto out_token;
+ /* send reply buffer to child */
+ rc = send_to(reply_fd[1], outbuf,
+ sizeof(outbuf));
+ if (rc != 0)
+ goto out_token;
+
+out_token:
+ free(gss_token);
+ goto out_pipe;
+ }
+
+ logmsg(LL_TRACE, "forked child %d\n", child);
+ }
+out_pipe:
+ close(req_fd[0]);
+ close(req_fd[1]);
+ close(reply_fd[0]);
+ close(reply_fd[1]);
+ return rc;
+ } else {
+ if (uparam.kup_pid)
+ logmsg(LL_TRACE, "caller's namespace is the same\n");
+ else
+ logmsg(LL_TRACE, "stick with current namespace\n");
+
+ rc = prepare_and_instantiate(cred, keyid, uparam.kup_uid);
+ if (rc != 0)
+ return rc;
+
+ /*
+ * fork a child to do the real gss negotiation
+ */
+ child = fork();
+ if (child == -1) {
+ logmsg(LL_ERR, "key %08x: can't create child: %s\n",
+ keyid, strerror(errno));
+ return 1;
+ } else if (child == 0) {
+ return lgssc_kr_negotiate(keyid, cred, &uparam,
+ req_fd, reply_fd);
+ }
+
+ logmsg(LL_TRACE, "forked child %d\n", child);
+ return 0;
+ }