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