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