Whamcloud - gitweb
LU-9243 gss: fix GSS struct definition badness
[fs/lustre-release.git] / lustre / utils / gss / gssd_proc.c
1 /*
2   gssd_proc.c
3
4   Copyright (c) 2000-2004 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
9   Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
10   Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
11   Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
12   All rights reserved, all wrongs reversed.
13
14   Redistribution and use in source and binary forms, with or without
15   modification, are permitted provided that the following conditions
16   are met:
17
18   1. Redistributions of source code must retain the above copyright
19      notice, this list of conditions and the following disclaimer.
20   2. Redistributions in binary form must reproduce the above copyright
21      notice, this list of conditions and the following disclaimer in the
22      documentation and/or other materials provided with the distribution.
23   3. Neither the name of the University nor the names of its
24      contributors may be used to endorse or promote products derived
25      from this software without specific prior written permission.
26
27   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
28   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39 */
40
41 #ifndef _GNU_SOURCE
42 #define _GNU_SOURCE
43 #endif
44 #include "config.h"
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/socket.h>
48 #include <arpa/inet.h>
49 #include <sys/fsuid.h>
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <pwd.h>
54 #include <grp.h>
55 #include <string.h>
56 #include <dirent.h>
57 #include <poll.h>
58 #include <fcntl.h>
59 #include <signal.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <gssapi/gssapi.h>
63 #ifdef HAVE_NETDB_H
64 # include <netdb.h>
65 #endif
66
67 #include <libcfs/util/param.h>
68 #include <uapi/linux/lustre/lgss.h>
69
70 #include "gssd.h"
71 #include "err_util.h"
72 #include "gss_util.h"
73 #include "gss_oids.h"
74 #include "krb5_util.h"
75 #include "context.h"
76 #include "lsupport.h"
77
78 /*
79  * pollarray:
80  *      array of struct pollfd suitable to pass to poll. initialized to
81  *      zero - a zero struct is ignored by poll() because the events mask is 0.
82  *
83  * clnt_list:
84  *      linked list of struct clnt_info which associates a clntXXX directory
85  *      with an index into pollarray[], and other basic data about that client.
86  *
87  * Directory structure: created by the kernel nfs client
88  *      {pipefs_nfsdir}/clntXX             : one per rpc_clnt struct in the kernel
89  *      {pipefs_nfsdir}/clntXX/krb5        : read uid for which kernel wants
90  *                                          a context, write the resulting context
91  *      {pipefs_nfsdir}/clntXX/info        : stores info such as server name
92  *
93  * Algorithm:
94  *      Poll all {pipefs_nfsdir}/clntXX/krb5 files.  When ready, data read
95  *      is a uid; performs rpcsec_gss context initialization protocol to
96  *      get a cred for that user.  Writes result to corresponding krb5 file
97  *      in a form the kernel code will understand.
98  *      In addition, we make sure we are notified whenever anything is
99  *      created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
100  *      and rescan the whole {pipefs_nfsdir} when this happens.
101  */
102
103 TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
104
105 struct pollfd * pollarray;
106
107 int pollsize;  /* the size of pollaray (in pollfd's) */
108
109 static void
110 destroy_client(struct clnt_info *clp)
111 {
112         printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
113         if (clp->krb5_poll_index != -1)
114                 memset(&pollarray[clp->krb5_poll_index], 0,
115                                         sizeof(struct pollfd));
116         if (clp->spkm3_poll_index != -1)
117                 memset(&pollarray[clp->spkm3_poll_index], 0,
118                                         sizeof(struct pollfd));
119         if (clp->dir_fd != -1) close(clp->dir_fd);
120         if (clp->krb5_fd != -1) close(clp->krb5_fd);
121         if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
122         if (clp->dirname) free(clp->dirname);
123         if (clp->servicename) free(clp->servicename);
124         free(clp);
125 }
126
127 static struct clnt_info *
128 insert_new_clnt(void)
129 {
130         struct clnt_info        *clp = NULL;
131
132         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
133                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
134                          strerror(errno));
135                 goto out;
136         }
137         clp->krb5_poll_index = -1;
138         clp->spkm3_poll_index = -1;
139         clp->krb5_fd = -1;
140         clp->spkm3_fd = -1;
141         clp->dir_fd = -1;
142
143         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
144 out:
145         return clp;
146 }
147
148 static int
149 process_clnt_dir_files(struct clnt_info * clp)
150 {
151         char    kname[32];
152         char    sname[32];
153
154         if (clp->krb5_fd == -1) {
155                 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
156                 clp->krb5_fd = open(kname, O_RDWR);
157         }
158         if (clp->spkm3_fd == -1) {
159                 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
160                 clp->spkm3_fd = open(sname, O_RDWR);
161         }
162         if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
163                 return -1;
164         return 0;
165 }
166
167 static int
168 get_poll_index(int *ind)
169 {
170         int i;
171
172         *ind = -1;
173         for (i=0; i<FD_ALLOC_BLOCK; i++) {
174                 if (pollarray[i].events == 0) {
175                         *ind = i;
176                         break;
177                 }
178         }
179         if (*ind == -1) {
180                 printerr(0, "ERROR: No pollarray slots open\n");
181                 return -1;
182         }
183         return 0;
184 }
185
186
187 static int
188 insert_clnt_poll(struct clnt_info *clp)
189 {
190         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
191                 if (get_poll_index(&clp->krb5_poll_index)) {
192                         printerr(0, "ERROR: Too many krb5 clients\n");
193                         return -1;
194                 }
195                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
196                 pollarray[clp->krb5_poll_index].events |= POLLIN;
197                 printerr(2, "monitoring krb5 channel under %s\n",
198                          clp->dirname);
199         }
200
201         if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
202                 if (get_poll_index(&clp->spkm3_poll_index)) {
203                         printerr(0, "ERROR: Too many spkm3 clients\n");
204                         return -1;
205                 }
206                 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
207                 pollarray[clp->spkm3_poll_index].events |= POLLIN;
208         }
209
210         return 0;
211 }
212
213 static void
214 process_clnt_dir(char *dir)
215 {
216         struct clnt_info *      clp;
217
218         if (!(clp = insert_new_clnt()))
219                 goto fail_destroy_client;
220
221         if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
222                 goto fail_destroy_client;
223         }
224         memcpy(clp->dirname, dir, strlen(dir));
225         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
226                 printerr(0, "ERROR: can't open %s: %s\n",
227                          clp->dirname, strerror(errno));
228                 goto fail_destroy_client;
229         }
230         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
231         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
232
233         if (process_clnt_dir_files(clp))
234                 goto fail_keep_client;
235
236         if (insert_clnt_poll(clp))
237                 goto fail_destroy_client;
238
239         return;
240
241 fail_destroy_client:
242         if (clp) {
243                 TAILQ_REMOVE(&clnt_list, clp, list);
244                 destroy_client(clp);
245         }
246 fail_keep_client:
247         /* We couldn't find some subdirectories, but we keep the client
248          * around in case we get a notification on the directory when the
249          * subdirectories are created. */
250 }
251
252 void
253 init_client_list(void)
254 {
255         TAILQ_INIT(&clnt_list);
256         /* Eventually plan to grow/shrink poll array: */
257         pollsize = FD_ALLOC_BLOCK;
258         pollarray = calloc(pollsize, sizeof(struct pollfd));
259 }
260
261 struct clnt_info *clnt_list_first_entry(void)
262 {
263         return clnt_list.tqh_first;
264 }
265
266 /*
267  * This is run after a DNOTIFY signal, and should clear up any
268  * directories that are no longer around, and re-scan any existing
269  * directories, since the DNOTIFY could have been in there.
270  */
271 static void
272 update_old_clients(struct dirent **namelist, int size)
273 {
274         struct clnt_info *clp;
275         void *saveprev;
276         int i, stillhere;
277
278         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
279                 stillhere = 0;
280                 for (i=0; i < size; i++) {
281                         if (!strcmp(clp->dirname, namelist[i]->d_name)) {
282                                 stillhere = 1;
283                                 break;
284                         }
285                 }
286                 if (!stillhere) {
287                         printerr(2, "destroying client %s\n", clp->dirname);
288                         saveprev = clp->list.tqe_prev;
289                         TAILQ_REMOVE(&clnt_list, clp, list);
290                         destroy_client(clp);
291                         clp = saveprev;
292                 }
293         }
294         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
295                 if (!process_clnt_dir_files(clp))
296                         insert_clnt_poll(clp);
297         }
298 }
299
300 /* Search for a client by directory name, return 1 if found, 0 otherwise */
301 static int
302 find_client(char *dirname)
303 {
304         struct clnt_info        *clp;
305
306         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
307                 if (!strcmp(clp->dirname, dirname))
308                         return 1;
309         return 0;
310 }
311
312 /* Used to read (and re-read) list of clients, set up poll array. */
313 int
314 update_client_list(void)
315 {
316         char lustre_dir[PATH_MAX];
317         struct dirent lustre_dirent = { .d_name = "lustre" };
318         struct dirent *namelist[1];
319         struct stat statbuf;
320         int i, j;
321
322         if (chdir(pipefs_dir) < 0) {
323                 printerr(0, "ERROR: can't chdir to %s: %s\n",
324                          pipefs_dir, strerror(errno));
325                 return -1;
326         }
327
328         snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
329         if (stat(lustre_dir, &statbuf) == 0) {
330                 namelist[0] = &lustre_dirent;
331                 j = 1;
332                 printerr(2, "re-processing lustre directory\n");
333         } else {
334                 namelist[0] = NULL;
335                 j = 0;
336                 printerr(2, "lustre directory not exist\n");
337         }
338
339         update_old_clients(namelist, j);
340         for (i=0; i < j; i++) {
341                 if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
342                         process_clnt_dir(namelist[i]->d_name);
343         }
344
345         chdir("/");
346         return 0;
347 }
348
349 /* Context creation response. */
350 struct lustre_gss_init_res {
351         gss_buffer_desc gr_ctx;         /* context handle */
352         unsigned int    gr_major;       /* major status */
353         unsigned int    gr_minor;       /* minor status */
354         unsigned int    gr_win;         /* sequence window */
355         gss_buffer_desc gr_token;       /* token */
356 };
357
358 struct lustre_gss_data {
359         int             lgd_established;
360         int             lgd_lustre_svc; /* mds/oss */
361         int             lgd_uid;        /* uid */
362         char           *lgd_uuid;       /* client device uuid */
363         gss_name_t      lgd_name;       /* service name */
364
365         gss_OID         lgd_mech;       /* mech OID */
366         unsigned int    lgd_req_flags;  /* request flags */
367         gss_cred_id_t   lgd_cred;       /* credential */
368         gss_ctx_id_t    lgd_ctx;        /* session context */
369         gss_buffer_desc lgd_rmt_ctx;    /* remote handle of context */
370         uint32_t        lgd_seq_win;    /* sequence window */
371
372         int             lgd_rpc_err;
373         int             lgd_gss_err;
374 };
375
376 static int
377 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
378             struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
379 {
380         char    *buf = NULL, *p = NULL, *end = NULL;
381         unsigned int timeout = 0; /* XXX decide on a reasonable value */
382         unsigned int buf_size = 0;
383
384         printerr(2, "doing downcall\n");
385         buf_size = sizeof(updata->seq) + sizeof(timeout) +
386                 sizeof(lgd->lgd_seq_win) +
387                 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
388                 sizeof(context_token->length) + context_token->length;
389         p = buf = malloc(buf_size);
390         end = buf + buf_size;
391
392         if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
393         /* Not setting any timeout for now: */
394         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
395         if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
396         if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
397         if (write_buffer(&p, end, context_token)) goto out_err;
398
399         lgssd_mutex_get(lgssd_mutex_downcall);
400         if (write(k5_fd, buf, p - buf) < p - buf) {
401                 lgssd_mutex_put(lgssd_mutex_downcall);
402                 goto out_err;
403         }
404         lgssd_mutex_put(lgssd_mutex_downcall);
405
406         if (buf) free(buf);
407         return 0;
408 out_err:
409         if (buf) free(buf);
410         printerr(0, "ERROR: Failed to write downcall!\n");
411         return -1;
412 }
413
414 static int
415 do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
416 {
417         char    buf[1024];
418         char    *p = buf, *end = buf + 1024;
419         unsigned int timeout = 0;
420         int     zero = 0;
421
422         printerr(1, "doing error downcall\n");
423
424         if (WRITE_BYTES(&p, end, seq)) goto out_err;
425         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
426         /* use seq_win = 0 to indicate an error: */
427         if (WRITE_BYTES(&p, end, zero)) goto out_err;
428         if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
429         if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
430
431         lgssd_mutex_get(lgssd_mutex_downcall);
432         if (write(k5_fd, buf, p - buf) < p - buf) {
433                 lgssd_mutex_put(lgssd_mutex_downcall);
434                 goto out_err;
435         }
436         lgssd_mutex_put(lgssd_mutex_downcall);
437         return 0;
438 out_err:
439         printerr(0, "Failed to write error downcall!\n");
440         return -1;
441 }
442
443 static
444 int do_negotiation(struct lustre_gss_data *lgd,
445                    gss_buffer_desc *gss_token,
446                    struct lustre_gss_init_res *gr,
447                    int timeout)
448 {
449         struct lgssd_ioctl_param param;
450         struct passwd *pw;
451         char outbuf[8192];
452         unsigned int *p;
453         glob_t path;
454         int fd;
455         int rc;
456
457         pw = getpwuid(lgd->lgd_uid);
458         if (!pw) {
459                 printerr(0, "no uid %u in local user database\n",
460                          lgd->lgd_uid);
461                 return -1;
462         }
463
464         param.version = GSSD_INTERFACE_VERSION_V1;
465         param.uuid = lgd->lgd_uuid;
466         param.lustre_svc = lgd->lgd_lustre_svc;
467         param.uid = lgd->lgd_uid;
468         param.gid = pw->pw_gid;
469         param.send_token_size = gss_token->length;
470         param.send_token = (char *) gss_token->value;
471         param.reply_buf_size = sizeof(outbuf);
472         param.reply_buf = outbuf;
473
474         if (cfs_get_param_paths(&path, "sptlrpc/gss/init_channel") != 0)
475                 return -1;
476
477         fd = open(path.gl_pathv[0], O_RDWR);
478         if (fd < 0) {
479                 printerr(0, "can't open file %s\n", path.gl_pathv[0]);
480                 rc = -1;
481                 goto out_params;
482         }
483
484         rc = write(fd, &param, sizeof(param));
485         if (rc != sizeof(param)) {
486                 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
487                 rc = -1;
488                 goto out_fd;
489         }
490         if (param.status) {
491                 printerr(0, "status: %d (%s)\n",
492                          param.status, strerror((int)param.status));
493                 if (param.status == -ETIMEDOUT) {
494                         /* kernel return -ETIMEDOUT means the rpc timedout,
495                          * we should notify the caller to reinitiate the
496                          * gss negotiation, by return -ERESTART
497                          */
498                         lgd->lgd_rpc_err = -ERESTART;
499                         lgd->lgd_gss_err = 0;
500                 } else {
501                         lgd->lgd_rpc_err = param.status;
502                         lgd->lgd_gss_err = 0;
503                 }
504                 rc = -1;
505                 goto out_fd;
506         }
507         p = (unsigned int *)outbuf;
508         gr->gr_major = *p++;
509         gr->gr_minor = *p++;
510         gr->gr_win = *p++;
511
512         gr->gr_ctx.length = *p++;
513         gr->gr_ctx.value = malloc(gr->gr_ctx.length);
514         memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
515         p += (((gr->gr_ctx.length + 3) & ~3) / 4);
516
517         gr->gr_token.length = *p++;
518         gr->gr_token.value = malloc(gr->gr_token.length);
519         memcpy(gr->gr_token.value, p, gr->gr_token.length);
520         p += (((gr->gr_token.length + 3) & ~3) / 4);
521
522         printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
523                  gr->gr_ctx.length, gr->gr_token.length);
524         rc = 0;
525 out_fd:
526         close(fd);
527 out_params:
528         cfs_free_param_data(&path);
529         return rc;
530 }
531
532 static
533 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
534 {
535         struct lustre_gss_init_res gr;
536         gss_buffer_desc         *recv_tokenp, send_token;
537         OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
538
539         /* GSS context establishment loop. */
540         memset(&gr, 0, sizeof(gr));
541         recv_tokenp = GSS_C_NO_BUFFER;
542
543         for (;;) {
544                 /* print the token we just received */
545                 if (recv_tokenp != GSS_C_NO_BUFFER) {
546                         printerr(3, "The received token length %d\n",
547                                  recv_tokenp->length);
548                         print_hexl(3, recv_tokenp->value, recv_tokenp->length);
549                 }
550
551                 maj_stat = gss_init_sec_context(&min_stat,
552                                                 lgd->lgd_cred,
553                                                 &lgd->lgd_ctx,
554                                                 lgd->lgd_name,
555                                                 lgd->lgd_mech,
556                                                 lgd->lgd_req_flags,
557                                                 0,              /* time req */
558                                                 NULL,           /* channel */
559                                                 recv_tokenp,
560                                                 NULL,           /* used mech */
561                                                 &send_token,
562                                                 &ret_flags,
563                                                 NULL);          /* time rec */
564
565                 if (recv_tokenp != GSS_C_NO_BUFFER) {
566                         gss_release_buffer(&min_stat, &gr.gr_token);
567                         recv_tokenp = GSS_C_NO_BUFFER;
568                 }
569                 if (maj_stat != GSS_S_COMPLETE &&
570                     maj_stat != GSS_S_CONTINUE_NEEDED) {
571                         pgsserr("gss_init_sec_context", maj_stat, min_stat,
572                                 lgd->lgd_mech);
573                         break;
574                 }
575                 if (send_token.length != 0) {
576                         memset(&gr, 0, sizeof(gr));
577
578                         /* print the token we are about to send */
579                         printerr(3, "token being sent length %d\n",
580                                  send_token.length);
581                         print_hexl(3, send_token.value, send_token.length);
582
583                         call_stat = do_negotiation(lgd, &send_token, &gr, 0);
584                         gss_release_buffer(&min_stat, &send_token);
585
586                         if (call_stat != 0 ||
587                             (gr.gr_major != GSS_S_COMPLETE &&
588                              gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
589                                 printerr(0, "call stat %d, major stat 0x%x\n",
590                                          (int)call_stat, gr.gr_major);
591                                 return -1;
592                         }
593
594                         if (gr.gr_ctx.length != 0) {
595                                 if (lgd->lgd_rmt_ctx.value)
596                                         gss_release_buffer(&min_stat,
597                                                            &lgd->lgd_rmt_ctx);
598                                 lgd->lgd_rmt_ctx = gr.gr_ctx;
599                         }
600                         if (gr.gr_token.length != 0) {
601                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
602                                         break;
603                                 recv_tokenp = &gr.gr_token;
604                         }
605                 }
606
607                 /* GSS_S_COMPLETE => check gss header verifier,
608                  * usually checked in gss_validate
609                  */
610                 if (maj_stat == GSS_S_COMPLETE) {
611                         lgd->lgd_established = 1;
612                         lgd->lgd_seq_win = gr.gr_win;
613                         break;
614                 }
615         }
616         /* End context negotiation loop. */
617         if (!lgd->lgd_established) {
618                 if (gr.gr_token.length != 0)
619                         gss_release_buffer(&min_stat, &gr.gr_token);
620
621                 printerr(0, "context negotiation failed\n");
622                 return -1;
623         }
624
625         printerr(2, "successfully refreshed lgd\n");
626         return 0;
627 }
628
629 static
630 int gssd_create_lgd(struct clnt_info *clp,
631                     struct lustre_gss_data *lgd,
632                     struct lgssd_upcall_data *updata,
633                     int authtype)
634 {
635         gss_buffer_desc         sname;
636         OM_uint32               maj_stat, min_stat;
637         int                     retval = -1;
638
639         lgd->lgd_established = 0;
640         lgd->lgd_lustre_svc = updata->svc;
641         lgd->lgd_uid = updata->uid;
642         lgd->lgd_uuid = updata->obd;
643
644         switch (authtype) {
645         case AUTHTYPE_KRB5:
646                 lgd->lgd_mech = (gss_OID) &krb5oid;
647                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
648                 break;
649         case AUTHTYPE_SPKM3:
650                 lgd->lgd_mech = (gss_OID) &spkm3oid;
651                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
652                  * Need a way to switch....
653                  */
654                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
655                 break;
656         default:
657                 printerr(0, "Invalid authentication type (%d)\n", authtype);
658                 return -1;
659         }
660
661         lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
662         lgd->lgd_ctx = GSS_C_NO_CONTEXT;
663         lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
664         lgd->lgd_seq_win = 0;
665
666         sname.value = clp->servicename;
667         sname.length = strlen(clp->servicename);
668
669         maj_stat = gss_import_name(&min_stat, &sname,
670                                    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
671                                    &lgd->lgd_name);
672         if (maj_stat != GSS_S_COMPLETE) {
673                 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
674                 goto out_fail;
675         }
676
677         retval = gssd_refresh_lgd(lgd);
678
679         if (lgd->lgd_name != GSS_C_NO_NAME)
680                 gss_release_name(&min_stat, &lgd->lgd_name);
681
682         if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
683                 gss_release_cred(&min_stat, &lgd->lgd_cred);
684
685   out_fail:
686         return retval;
687 }
688
689 static
690 void gssd_free_lgd(struct lustre_gss_data *lgd)
691 {
692         gss_buffer_t            token = GSS_C_NO_BUFFER;
693         OM_uint32               maj_stat, min_stat;
694
695         if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
696                 return;
697
698         maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
699 }
700
701 static
702 int construct_service_name(struct clnt_info *clp,
703                            struct lgssd_upcall_data *ud)
704 {
705         const int buflen = 256;
706         char name[buflen];
707
708         if (clp->servicename) {
709                 free(clp->servicename);
710                 clp->servicename = NULL;
711         }
712
713         if (lnet_nid2hostname(ud->nid, name, buflen))
714                 return -1;
715
716         clp->servicename = malloc(32 + strlen(name));
717         if (!clp->servicename) {
718                 printerr(0, "can't alloc memory\n");
719                 return -1;
720         }
721         sprintf(clp->servicename, "%s@%s",
722                 ud->svc == LUSTRE_GSS_SVC_MDS ?
723                 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
724                 name);
725         printerr(2, "constructed servicename: %s\n", clp->servicename);
726         return 0;
727 }
728
729 /*
730  * this code uses the userland rpcsec gss library to create a krb5
731  * context on behalf of the kernel
732  */
733 void
734 handle_krb5_upcall(struct clnt_info *clp)
735 {
736         pid_t                   pid;
737         gss_buffer_desc         token = { 0, NULL };
738         struct lgssd_upcall_data updata;
739         struct lustre_gss_data  lgd;
740         char                    **credlist = NULL;
741         char                    **ccname;
742         int                     read_rc;
743
744         printerr(2, "handling krb5 upcall\n");
745
746         memset(&lgd, 0, sizeof(lgd));
747         lgd.lgd_rpc_err = -EPERM; /* default error code */
748
749         read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
750         if (read_rc < 0) {
751                 printerr(0, "WARNING: failed reading from krb5 "
752                             "upcall pipe: %s\n", strerror(errno));
753                 return;
754         } else if (read_rc != sizeof(updata)) {
755                 printerr(0, "upcall data mismatch: length %d, expect %d\n",
756                          read_rc, sizeof(updata));
757
758                 /* the sequence number must be the first field. if read >= 4
759                  * bytes then we know at least sequence is fine, try to send
760                  * error notification nicely.
761                  */
762                 if (read_rc >= 4)
763                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
764                 return;
765         }
766
767         /* FIXME temporary fix, do this before fork.
768          * in case of errors could have memory leak!!!
769          */
770         if (updata.uid == 0) {
771                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
772                         printerr(0, "ERROR: Failed to obtain machine "
773                                     "credentials\n");
774                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
775                         return;
776                 }
777         }
778
779         /* fork child process */
780         pid = fork();
781         if (pid < 0) {
782                 printerr(0, "can't fork: %s\n", strerror(errno));
783                 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
784                 return;
785         } else if (pid > 0) {
786                 printerr(2, "forked child process: %d\n", pid);
787                 return;
788         }
789
790         printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
791                  updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
792
793         if (updata.svc != LUSTRE_GSS_SVC_MDS &&
794             updata.svc != LUSTRE_GSS_SVC_OSS) {
795                 printerr(0, "invalid svc %d\n", updata.svc);
796                 lgd.lgd_rpc_err = -EPROTO;
797                 goto out_return_error;
798         }
799         updata.obd[sizeof(updata.obd)-1] = '\0';
800
801         if (construct_service_name(clp, &updata)) {
802                 printerr(0, "failed to construct service name\n");
803                 goto out_return_error;
804         }
805
806         if (updata.uid == 0) {
807                 int success = 0;
808
809                 /*
810                  * Get a list of credential cache names and try each
811                  * of them until one works or we've tried them all
812                  */
813 /*
814                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
815                         printerr(0, "ERROR: Failed to obtain machine "
816                                     "credentials for %s\n", clp->servicename);
817                         goto out_return_error;
818                 }
819 */
820                 for (ccname = credlist; ccname && *ccname; ccname++) {
821                         gssd_setup_krb5_machine_gss_ccache(*ccname);
822                         if ((gssd_create_lgd(clp, &lgd, &updata,
823                                              AUTHTYPE_KRB5)) == 0) {
824                                 /* Success! */
825                                 success++;
826                                 break;
827                         }
828                         printerr(2, "WARNING: Failed to create krb5 context "
829                                     "for user with uid %d with credentials "
830                                     "cache %s for service %s\n",
831                                  updata.uid, *ccname, clp->servicename);
832                 }
833                 gssd_free_krb5_machine_cred_list(credlist);
834                 if (!success) {
835                         printerr(0, "ERROR: Failed to create krb5 context "
836                                     "for user with uid %d with any "
837                                     "credentials cache for service %s\n",
838                                  updata.uid, clp->servicename);
839                         goto out_return_error;
840                 }
841         }
842         else {
843                 /* Tell krb5 gss which credentials cache to use */
844                 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
845
846                 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
847                         printerr(0, "WARNING: Failed to create krb5 context "
848                                     "for user with uid %d for service %s\n",
849                                  updata.uid, clp->servicename);
850                         goto out_return_error;
851                 }
852         }
853
854         if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
855                 printerr(0, "WARNING: Failed to serialize krb5 context for "
856                             "user with uid %d for service %s\n",
857                          updata.uid, clp->servicename);
858                 goto out_return_error;
859         }
860
861         printerr(1, "refreshed: %u@%s for %s\n",
862                  updata.uid, updata.obd, clp->servicename);
863         do_downcall(clp->krb5_fd, &updata, &lgd, &token);
864
865 out:
866         if (token.value)
867                 free(token.value);
868
869         gssd_free_lgd(&lgd);
870         exit(0); /* i'm child process */
871
872 out_return_error:
873         do_error_downcall(clp->krb5_fd, updata.seq,
874                           lgd.lgd_rpc_err, lgd.lgd_gss_err);
875         goto out;
876 }
877