Whamcloud - gitweb
branch: HEAD
[fs/lustre-release.git] / lustre / utils / gss / lgss_keyring.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  lucall_keyring.c
5  *  user-space upcall to create GSS context, using keyring interface to kernel
6  *
7  *  Copyright (c) 2007 Cluster File Systems, Inc.
8  *   Author: Eric Mei <ericm@clusterfs.com>
9  *
10  *   This file is part of the Lustre file system, http://www.lustre.org
11  *   Lustre is a trademark of Cluster File Systems, Inc.
12  *
13  *   You may have signed or agreed to another license before downloading
14  *   this software.  If so, you are bound by the terms and conditions
15  *   of that agreement, and the following does not apply to you.  See the
16  *   LICENSE file included with this distribution for more information.
17  *
18  *   If you did not agree to a different license, then this copy of Lustre
19  *   is open source software; you can redistribute it and/or modify it
20  *   under the terms of version 2 of the GNU General Public License as
21  *   published by the Free Software Foundation.
22  *
23  *   In either case, Lustre is distributed in the hope that it will be
24  *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
25  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *   license text for more details.
27  */
28
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <pwd.h>
36 #include <keyutils.h>
37 #include <gssapi/gssapi.h>
38
39 #include "lsupport.h"
40 #include "lgss_utils.h"
41 #include "write_bytes.h"
42 #include "context.h"
43
44 /*
45  * gss target string of lustre service we are negotiating for
46  */
47 static char *g_service = NULL;
48
49 /*
50  * all data about negotiation
51  */
52 struct lgss_nego_data {
53         uint32_t        lnd_established:1;
54
55         int             lnd_secid;
56         uint32_t        lnd_uid;
57         uint32_t        lnd_lsvc;
58         char           *lnd_uuid;
59
60         gss_OID         lnd_mech;               /* mech OID */
61         gss_name_t      lnd_svc_name;           /* service name */
62         u_int           lnd_req_flags;          /* request flags */
63         gss_cred_id_t   lnd_cred;               /* credential */
64         gss_ctx_id_t    lnd_ctx;                /* session context */
65         gss_buffer_desc lnd_rmt_ctx;            /* remote handle of context */
66         uint32_t        lnd_seq_win;            /* sequence window */
67
68         int             lnd_rpc_err;
69         int             lnd_gss_err;
70 };
71
72 /*
73  * context creation response
74  */
75 struct lgss_init_res {
76         gss_buffer_desc gr_ctx;         /* context handle */
77         u_int           gr_major;       /* major status */
78         u_int           gr_minor;       /* minor status */
79         u_int           gr_win;         /* sequence window */
80         gss_buffer_desc gr_token;       /* token */
81 };
82
83 struct keyring_upcall_param {
84         uint32_t        kup_ver;
85         uint32_t        kup_secid;
86         uint32_t        kup_uid;
87         uint32_t        kup_fsuid;
88         uint32_t        kup_gid;
89         uint32_t        kup_fsgid;
90         uint32_t        kup_svc;
91         uint64_t        kup_nid;
92         char            kup_tgt[64];
93         char            kup_mech[16];
94         int             kup_is_root;
95         int             kup_is_mds;
96 };
97
98 /****************************************
99  * child process: gss negotiation       *
100  ****************************************/
101
102 #define INIT_CHANNEL    "/proc/fs/lustre/sptlrpc/gss/init_channel"
103
104 int do_nego_rpc(struct lgss_nego_data *lnd,
105                 gss_buffer_desc *gss_token,
106                 struct lgss_init_res *gr)
107 {
108         struct lgssd_ioctl_param  param;
109         struct passwd            *pw;
110         int                       fd, ret, res;
111         char                      outbuf[8192];
112         unsigned int             *p;
113
114         logmsg(LL_TRACE, "start negotiation rpc\n");
115
116         pw = getpwuid(lnd->lnd_uid);
117         if (!pw) {
118                 logmsg(LL_ERR, "no uid %u in local user database\n",
119                        lnd->lnd_uid);
120                 return -EACCES;
121         }
122
123         param.version = GSSD_INTERFACE_VERSION;
124         param.secid = lnd->lnd_secid;
125         param.uuid = lnd->lnd_uuid;
126         param.lustre_svc = lnd->lnd_lsvc;
127         param.uid = lnd->lnd_uid;
128         param.gid = pw->pw_gid;
129         param.send_token_size = gss_token->length;
130         param.send_token = (char *) gss_token->value;
131         param.reply_buf_size = sizeof(outbuf);
132         param.reply_buf = outbuf;
133
134         logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
135
136         fd = open(INIT_CHANNEL, O_WRONLY);
137         if (fd < 0) {
138                 logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
139                 return -EACCES;
140         }
141
142         logmsg(LL_TRACE, "to down-write\n");
143
144         ret = write(fd, &param, sizeof(param));
145         if (ret != sizeof(param)) {
146                 logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
147                 close(fd);
148                 return -EACCES;
149         }
150         close(fd);
151
152         logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
153         if (param.status) {
154                 logmsg(LL_ERR, "status: %d (%s)\n",
155                        param.status, strerror((int)param.status));
156
157                 /* kernel return -ETIMEDOUT means the rpc timedout, we should
158                  * notify the caller to reinitiate the gss negotiation, by
159                  * returning -ERESTART
160                  */
161                 if (param.status == -ETIMEDOUT)
162                         return -ERESTART;
163                 else
164                         return param.status;
165         }
166
167         p = (unsigned int *)outbuf;
168         res = *p++;
169         gr->gr_major = *p++;
170         gr->gr_minor = *p++;
171         gr->gr_win = *p++;
172
173         gr->gr_ctx.length = *p++;
174         gr->gr_ctx.value = malloc(gr->gr_ctx.length);
175         memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
176         p += (((gr->gr_ctx.length + 3) & ~3) / 4);
177
178         gr->gr_token.length = *p++;
179         gr->gr_token.value = malloc(gr->gr_token.length);
180         memcpy(gr->gr_token.value, p, gr->gr_token.length);
181         p += (((gr->gr_token.length + 3) & ~3) / 4);
182
183         logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
184                gr->gr_ctx.length, gr->gr_token.length);
185         return 0;
186 }
187
188 /*
189  * if return error, the lnd_rpc_err or lnd_gss_err is set.
190  */
191 static int lgssc_negotiation(struct lgss_nego_data *lnd)
192 {
193         struct lgss_init_res    gr;
194         gss_buffer_desc        *recv_tokenp, send_token;
195         OM_uint32               maj_stat, min_stat, ret_flags;
196
197         logmsg(LL_TRACE, "start gss negotiation\n");
198
199         /* GSS context establishment loop. */
200         memset(&gr, 0, sizeof(gr));
201         recv_tokenp = GSS_C_NO_BUFFER;
202
203         for (;;) {
204                 maj_stat = gss_init_sec_context(&min_stat,
205                                                 lnd->lnd_cred,
206                                                 &lnd->lnd_ctx,
207                                                 lnd->lnd_svc_name,
208                                                 lnd->lnd_mech,
209                                                 lnd->lnd_req_flags,
210                                                 0,            /* time req */
211                                                 NULL,         /* channel */
212                                                 recv_tokenp,
213                                                 NULL,         /* used mech */
214                                                 &send_token,
215                                                 &ret_flags,
216                                                 NULL);        /* time rec */
217
218                 if (recv_tokenp != GSS_C_NO_BUFFER) {
219                         gss_release_buffer(&min_stat, &gr.gr_token);
220                         recv_tokenp = GSS_C_NO_BUFFER;
221                 }
222
223                 if (maj_stat != GSS_S_COMPLETE &&
224                     maj_stat != GSS_S_CONTINUE_NEEDED) {
225                         lnd->lnd_gss_err = maj_stat;
226
227                         logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
228                                    "failed init context");
229                         break;
230                 }
231
232                 if (send_token.length != 0) {
233                         memset(&gr, 0, sizeof(gr));
234
235                         lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr);
236                         gss_release_buffer(&min_stat, &send_token);
237
238                         if (lnd->lnd_rpc_err) {
239                                 logmsg(LL_ERR, "negotiation rpc error: %d\n",
240                                        lnd->lnd_rpc_err);
241                                 return -1;
242                         }
243
244                         if (gr.gr_major != GSS_S_COMPLETE &&
245                             gr.gr_major != GSS_S_CONTINUE_NEEDED) {
246                                 lnd->lnd_gss_err = gr.gr_major;
247
248                                 logmsg(LL_ERR, "negotiation gss error %x\n",
249                                        lnd->lnd_gss_err);
250                                 return -1;
251                         }
252
253                         if (gr.gr_ctx.length != 0) {
254                                 if (lnd->lnd_rmt_ctx.value)
255                                         gss_release_buffer(&min_stat,
256                                                            &lnd->lnd_rmt_ctx);
257                                 lnd->lnd_rmt_ctx = gr.gr_ctx;
258                         }
259
260                         if (gr.gr_token.length != 0) {
261                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
262                                         break;
263                                 recv_tokenp = &gr.gr_token;
264                         }
265                 }
266
267                 /* GSS_S_COMPLETE => check gss header verifier,
268                  * usually checked in gss_validate
269                  */
270                 if (maj_stat == GSS_S_COMPLETE) {
271                         lnd->lnd_established = 1;
272                         lnd->lnd_seq_win = gr.gr_win;
273                         break;
274                 }
275         }
276
277         /* End context negotiation loop. */
278         if (!lnd->lnd_established) {
279                 if (gr.gr_token.length != 0)
280                         gss_release_buffer(&min_stat, &gr.gr_token);
281
282                 if (lnd->lnd_gss_err == GSS_S_COMPLETE)
283                         lnd->lnd_rpc_err = -EACCES;
284
285                 logmsg(LL_ERR, "context negotiation failed\n");
286                 return -1;
287         }
288
289         logmsg(LL_DEBUG, "successfully negotiated a context\n");
290         return 0;
291 }
292
293 /*
294  * if return error, the lnd_rpc_err or lnd_gss_err is set.
295  */
296 static int lgssc_init_nego_data(struct lgss_nego_data *lnd,
297                                 struct keyring_upcall_param *kup,
298                                 lgss_mech_t mech)
299 {
300         gss_buffer_desc         sname;
301         OM_uint32               maj_stat, min_stat;
302
303         memset(lnd, 0, sizeof(*lnd));
304
305         lnd->lnd_secid = kup->kup_secid;
306         lnd->lnd_uid = kup->kup_uid;
307         lnd->lnd_lsvc = kup->kup_svc;
308         lnd->lnd_uuid = kup->kup_tgt;
309
310         lnd->lnd_established = 0;
311         lnd->lnd_svc_name = GSS_C_NO_NAME;
312         lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
313         lnd->lnd_ctx = GSS_C_NO_CONTEXT;
314         lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
315         lnd->lnd_seq_win = 0;
316
317         switch (mech) {
318         case LGSS_MECH_KRB5:
319                 lnd->lnd_mech = (gss_OID) &krb5oid;
320                 lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
321                 break;
322         default:
323                 logmsg(LL_ERR, "invalid mech: %d\n", mech);
324                 lnd->lnd_rpc_err = -EACCES;
325                 return -1;
326         }
327
328         sname.value = g_service;
329         sname.length = strlen(g_service);
330
331         maj_stat = gss_import_name(&min_stat, &sname,
332                                    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
333                                    &lnd->lnd_svc_name);
334         if (maj_stat != GSS_S_COMPLETE) {
335                 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
336                            "can't import svc name");
337                 lnd->lnd_gss_err = maj_stat;
338                 return -1;
339         }
340
341         return 0;
342 }
343
344 void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
345 {
346         OM_uint32       maj_stat, min_stat;
347
348         if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
349                 maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
350                 if (maj_stat != GSS_S_COMPLETE)
351                         logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
352                                    "can't release service name");
353         }
354
355         if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
356                 maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
357                 if (maj_stat != GSS_S_COMPLETE)
358                         logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
359                                    "can't release credential");
360         }
361 }
362
363 static
364 int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
365 {
366         int      seqwin = 0;
367         char     buf[32];
368         char    *p, *end;
369
370         logmsg(LL_TRACE, "revoking kernel key %08x\n", keyid);
371
372         p = buf;
373         end = buf + sizeof(buf);
374
375         WRITE_BYTES(&p, end, seqwin);
376         WRITE_BYTES(&p, end, rpc_error);
377         WRITE_BYTES(&p, end, gss_error);
378
379 again:
380         if (keyctl_update(keyid, buf, p - buf)) {
381                 if (errno != EAGAIN) {
382                         logmsg(LL_ERR, "revoke key %08x: %s\n",
383                                keyid, strerror(errno));
384                         return -1;
385                 }
386
387                 logmsg(LL_WARN, "key %08x: revoking too soon, try again\n",
388                        keyid);
389                 sleep(2);
390                 goto again;
391         }
392
393         logmsg(LL_INFO, "key %08x: revoked\n", keyid);
394         return 0;
395 }
396
397 static
398 int update_kernel_key(key_serial_t keyid,
399                       struct lgss_nego_data *lnd,
400                       gss_buffer_desc *ctx_token)
401 {
402         char        *buf = NULL, *p = NULL, *end = NULL;
403         unsigned int buf_size = 0;
404         int          rc;
405
406         logmsg(LL_TRACE, "updating kernel key %08x\n", keyid);
407
408         buf_size = sizeof(lnd->lnd_seq_win) +
409                    sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
410                    sizeof(ctx_token->length) + ctx_token->length;
411         buf = malloc(buf_size);
412         if (buf == NULL) {
413                 logmsg(LL_ERR, "key %08x: can't alloc update buf: size %d\n",
414                        keyid, buf_size);
415                 return 1;
416         }
417
418         p = buf;
419         end = buf + buf_size;
420         rc = -1;
421
422         if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
423                 goto out;
424         if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
425                 goto out;
426         if (write_buffer(&p, end, ctx_token))
427                 goto out;
428
429 again:
430         if (keyctl_update(keyid, buf, p - buf)) {
431                 if (errno != EAGAIN) {
432                         logmsg(LL_ERR, "update key %08x: %s\n",
433                                keyid, strerror(errno));
434                         goto out;
435                 }
436
437                 logmsg(LL_DEBUG, "key %08x: updating too soon, try again\n",
438                        keyid);
439                 sleep(2);
440                 goto again;
441         }
442
443         rc = 0;
444         logmsg(LL_DEBUG, "key %08x: updated\n", keyid);
445 out:
446         free(buf);
447         return rc;
448 }
449
450 /*
451  * note we inherited assumed authority from parent process
452  */
453 static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
454                               struct keyring_upcall_param *kup)
455 {
456         struct lgss_nego_data   lnd;
457         gss_buffer_desc         token = GSS_C_EMPTY_BUFFER;
458         OM_uint32               min_stat;
459         int                     rc = -1;
460
461         logmsg(LL_TRACE, "child start on behalf of key %08x: "
462                "cred %p, uid %u, svc %u, nid %Lx, uids: %u:%u/%u:%u\n",
463                keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
464                kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
465
466         if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
467                 logmsg(LL_ERR, "key %08x: failed to construct service "
468                        "string\n", keyid);
469                 error_kernel_key(keyid, -EACCES, 0);
470                 goto out_cred;
471         }
472
473         if (lgss_using_cred(cred)) {
474                 logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
475                 error_kernel_key(keyid, -EACCES, 0);
476                 goto out_cred;
477         }
478
479         if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
480                 logmsg(LL_ERR, "key %08x: failed to initialize "
481                        "negotiation data\n", keyid);
482                 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
483                 goto out_cred;
484         }
485
486         rc = lgssc_negotiation(&lnd);
487         if (rc) {
488                 logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
489                 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
490                 goto out;
491         }
492
493         rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
494         if (rc) {
495                 logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
496                 error_kernel_key(keyid, rc, lnd.lnd_gss_err);
497                 goto out;
498         }
499
500         rc = update_kernel_key(keyid,  &lnd, &token);
501         if (rc)
502                 goto out;
503
504         rc = 0;
505         logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
506                keyid, kup->kup_uid);
507 out:
508         if (token.length != 0)
509                 gss_release_buffer(&min_stat, &token);
510
511         lgssc_fini_nego_data(&lnd);
512
513 out_cred:
514         lgss_release_cred(cred);
515         return rc;
516 }
517
518 /*
519  * call out info format: s[:s]...
520  *  [0]: secid          (uint)
521  *  [1]: mech_name      (string)
522  *  [2]: uid            (uint)
523  *  [3]: gid            (uint)
524  *  [4]: flags          (chars) FMT: r-root; m-mds
525  *  [5]: lustre_svc     (uint)
526  *  [6]: target_nid     (uint64)
527  *  [7]: target_uuid    (string)
528  */
529 static int parse_callout_info(const char *coinfo,
530                               struct keyring_upcall_param *uparam)
531 {
532         const int       nargs = 8;
533         char            buf[1024];
534         char           *string = buf;
535         int             length, i;
536         char           *data[nargs];
537         char           *pos;
538
539         length = strlen(coinfo) + 1;
540         if (length > 1024) {
541                 logmsg(LL_ERR, "coinfo too long\n");
542                 return -1;
543         }
544         memcpy(buf, coinfo, length);
545
546         for (i = 0; i < nargs - 1; i++) {
547                 pos = strchr(string, ':');
548                 if (pos == NULL) {
549                         logmsg(LL_ERR, "short of components\n");
550                         return -1;
551                 }
552
553                 *pos = '\0';
554                 data[i] = string;
555                 string = pos + 1;
556         }
557         data[i] = string;
558
559         logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%s,%s,%s\n",
560                data[0], data[1], data[2], data[3], data[4], data[5],
561                data[6], data[7]);
562
563         uparam->kup_secid = strtol(data[0], NULL, 0);
564         strncpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
565         uparam->kup_uid = strtol(data[2], NULL, 0);
566         uparam->kup_gid = strtol(data[3], NULL, 0);
567         if (strchr(data[4], 'r'))
568                 uparam->kup_is_root = 1;
569         if (strchr(data[4], 'm'))
570                 uparam->kup_is_mds = 1;
571         uparam->kup_svc = strtol(data[5], NULL, 0);
572         uparam->kup_nid = strtoll(data[6], NULL, 0);
573         strncpy(uparam->kup_tgt, data[7], sizeof(uparam->kup_tgt));
574
575         logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u "
576                "is_root %d, is_mds %d, svc %d, nid 0x%Lx, tgt %s\n",
577                uparam->kup_secid, uparam->kup_mech,
578                uparam->kup_uid, uparam->kup_gid,
579                uparam->kup_is_root, uparam->kup_is_mds, uparam->kup_svc,
580                uparam->kup_nid, uparam->kup_tgt);
581         return 0;
582 }
583
584 /****************************************
585  * main process                         *
586  ****************************************/
587
588 int main(int argc, char *argv[])
589 {
590         struct keyring_upcall_param     uparam;
591         key_serial_t                    keyid;
592         key_serial_t                    sring;
593         key_serial_t                    inst_keyring;
594         pid_t                           child;
595         struct lgss_mech_type          *mech;
596         struct lgss_cred               *cred;
597
598         /*
599          * parse & sanity check upcall parameters
600          * expected to be called with:
601          * [1]:  operation
602          * [2]:  key ID
603          * [3]:  key type
604          * [4]:  key description
605          * [5]:  call out info
606          * [6]:  UID
607          * [7]:  GID
608          * [8]:  thread keyring
609          * [9]:  process keyring
610          * [10]: session keyring
611          */
612         if (argc != 10 + 1) {
613                 logmsg(LL_ERR, "invalid parameter number %d\n", argc);
614                 return 1;
615         }
616
617         logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
618                argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);
619
620         memset(&uparam, 0, sizeof(uparam));
621
622         if (strcmp(argv[1], "create") != 0) {
623                 logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
624                 return 1;
625         }
626
627         if (sscanf(argv[2], "%d", &keyid) != 1) {
628                 logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
629                 return 1;
630         }
631
632         if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
633                 logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
634                 return 1;
635         }
636
637         if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
638                 logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
639                 return 1;
640         }
641
642         if (sscanf(argv[10], "%d", &sring) != 1) {
643                 logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
644                 return 1;
645         }
646
647         if (parse_callout_info(argv[5], &uparam)) {
648                 logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
649                 return 1;
650         }
651
652         logmsg(LL_TRACE, "parsing parameters OK\n");
653
654         /*
655          * prepare a cred
656          */
657         mech = lgss_name2mech(uparam.kup_mech);
658         if (mech == NULL) {
659                 logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
660                        keyid, uparam.kup_mech);
661                 return 1;
662         }
663
664         if (lgss_mech_initialize(mech)) {
665                 logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
666                        keyid, mech->lmt_name);
667                 return 1;
668         }
669
670         cred = lgss_create_cred(mech);
671         if (cred == NULL) {
672                 logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
673                        keyid, mech->lmt_name);
674                 return 1;
675         }
676
677         cred->lc_uid = uparam.kup_uid;
678         cred->lc_fl_root = (uparam.kup_is_root != 0);
679         cred->lc_fl_mds = (uparam.kup_is_mds != 0);
680         cred->lc_tgt_nid = uparam.kup_nid;
681         cred->lc_tgt_svc = uparam.kup_svc;
682
683         if (lgss_prepare_cred(cred)) {
684                 logmsg(LL_ERR, "key %08x: failed to prepare credentials "
685                        "for user %d\n", keyid, uparam.kup_uid);
686                 return 1;
687         }
688
689         /* pre initialize the key. note the keyring linked to is actually of the
690          * original requesting process, not _this_ upcall process. if it's for
691          * root user, don't link to any keyrings because we want fully control
692          * on it, and share it among all root sessions; otherswise link to
693          * session keyring.
694          */
695         if (cred->lc_fl_root || cred->lc_fl_mds)
696                 inst_keyring = 0;
697         else
698                 inst_keyring = KEY_SPEC_SESSION_KEYRING;
699
700         if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
701                 logmsg(LL_ERR, "instantiate key %08x: %s\n",
702                        keyid, strerror(errno));
703                 return 1;
704         }
705
706         logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
707
708         /*
709          * fork a child to do the real gss negotiation
710          */
711         child = fork();
712         if (child == -1) {
713                 logmsg(LL_ERR, "key %08x: can't create child: %s\n",
714                        keyid, strerror(errno));
715                 return 1;
716         } else if (child == 0) {
717                 return lgssc_kr_negotiate(keyid, cred, &uparam);
718         }
719
720         logmsg(LL_TRACE, "forked child %d\n", child);
721         return 0;
722 }