+ 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 {
+ logmsg(LL_TRACE, "caller's namespace is the same\n");