Whamcloud - gitweb
9d3f3e9fe3d31eafdc11c7a13f239d8ba127ff84
[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 }
248
249 void
250 init_client_list(void)
251 {
252         TAILQ_INIT(&clnt_list);
253         /* Eventually plan to grow/shrink poll array: */
254         pollsize = FD_ALLOC_BLOCK;
255         pollarray = calloc(pollsize, sizeof(struct pollfd));
256 }
257
258 /*
259  * This is run after a DNOTIFY signal, and should clear up any
260  * directories that are no longer around, and re-scan any existing
261  * directories, since the DNOTIFY could have been in there.
262  */
263 static void
264 update_old_clients(struct dirent **namelist, int size)
265 {
266         struct clnt_info *clp;
267         void *saveprev;
268         int i, stillhere;
269
270         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
271                 stillhere = 0;
272                 for (i=0; i < size; i++) {
273                         if (!strcmp(clp->dirname, namelist[i]->d_name)) {
274                                 stillhere = 1;
275                                 break;
276                         }
277                 }
278                 if (!stillhere) {
279                         printerr(2, "destroying client %s\n", clp->dirname);
280                         saveprev = clp->list.tqe_prev;
281                         TAILQ_REMOVE(&clnt_list, clp, list);
282                         destroy_client(clp);
283                         clp = saveprev;
284                 }
285         }
286         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
287                 if (!process_clnt_dir_files(clp))
288                         insert_clnt_poll(clp);
289         }
290 }
291
292 /* Search for a client by directory name, return 1 if found, 0 otherwise */
293 static int
294 find_client(char *dirname)
295 {
296         struct clnt_info        *clp;
297
298         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
299                 if (!strcmp(clp->dirname, dirname))
300                         return 1;
301         return 0;
302 }
303
304 /* Used to read (and re-read) list of clients, set up poll array. */
305 int
306 update_client_list(void)
307 {
308         char lustre_dir[PATH_MAX];
309         struct dirent lustre_dirent = { .d_name = "lustre" };
310         struct dirent *namelist[1];
311         struct stat statbuf;
312         int i, j;
313
314         if (chdir(pipefs_dir) < 0) {
315                 printerr(0, "ERROR: can't chdir to %s: %s\n",
316                          pipefs_dir, strerror(errno));
317                 return -1;
318         }
319
320         snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "lustre");
321         if (stat(lustre_dir, &statbuf) == 0) {
322                 namelist[0] = &lustre_dirent;
323                 j = 1;
324                 printerr(2, "re-processing lustre directory\n");
325         } else {
326                 namelist[0] = NULL;
327                 j = 0;
328                 printerr(2, "lustre directory not exist\n");
329         }
330
331         update_old_clients(namelist, j);
332         for (i=0; i < j; i++) {
333                 if (i < FD_ALLOC_BLOCK && !find_client(namelist[i]->d_name))
334                         process_clnt_dir(namelist[i]->d_name);
335         }
336
337         chdir("/");
338         return 0;
339 }
340
341 /* Context creation response. */
342 struct lustre_gss_init_res {
343         gss_buffer_desc gr_ctx;         /* context handle */
344         unsigned int    gr_major;       /* major status */
345         unsigned int    gr_minor;       /* minor status */
346         unsigned int    gr_win;         /* sequence window */
347         gss_buffer_desc gr_token;       /* token */
348 };
349
350 struct lustre_gss_data {
351         int             lgd_established;
352         int             lgd_lustre_svc; /* mds/oss */
353         int             lgd_uid;        /* uid */
354         char           *lgd_uuid;       /* client device uuid */
355         gss_name_t      lgd_name;       /* service name */
356
357         gss_OID         lgd_mech;       /* mech OID */
358         unsigned int    lgd_req_flags;  /* request flags */
359         gss_cred_id_t   lgd_cred;       /* credential */
360         gss_ctx_id_t    lgd_ctx;        /* session context */
361         gss_buffer_desc lgd_rmt_ctx;    /* remote handle of context */
362         uint32_t        lgd_seq_win;    /* sequence window */
363
364         int             lgd_rpc_err;
365         int             lgd_gss_err;
366 };
367
368 static int
369 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
370             struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
371 {
372         char    *buf = NULL, *p = NULL, *end = NULL;
373         unsigned int timeout = 0; /* XXX decide on a reasonable value */
374         unsigned int buf_size = 0;
375
376         printerr(2, "doing downcall\n");
377         buf_size = sizeof(updata->seq) + sizeof(timeout) +
378                 sizeof(lgd->lgd_seq_win) +
379                 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
380                 sizeof(context_token->length) + context_token->length;
381         p = buf = malloc(buf_size);
382         end = buf + buf_size;
383
384         if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
385         /* Not setting any timeout for now: */
386         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
387         if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
388         if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
389         if (write_buffer(&p, end, context_token)) goto out_err;
390
391         lgssd_mutex_get(lgssd_mutex_downcall);
392         if (write(k5_fd, buf, p - buf) < p - buf) {
393                 lgssd_mutex_put(lgssd_mutex_downcall);
394                 goto out_err;
395         }
396         lgssd_mutex_put(lgssd_mutex_downcall);
397
398         if (buf) free(buf);
399         return 0;
400 out_err:
401         if (buf) free(buf);
402         printerr(0, "ERROR: Failed to write downcall!\n");
403         return -1;
404 }
405
406 static int
407 do_error_downcall(int k5_fd, uint32_t seq, int rpc_err, int gss_err)
408 {
409         char    buf[1024];
410         char    *p = buf, *end = buf + 1024;
411         unsigned int timeout = 0;
412         int     zero = 0;
413
414         printerr(1, "doing error downcall\n");
415
416         if (WRITE_BYTES(&p, end, seq)) goto out_err;
417         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
418         /* use seq_win = 0 to indicate an error: */
419         if (WRITE_BYTES(&p, end, zero)) goto out_err;
420         if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
421         if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
422
423         lgssd_mutex_get(lgssd_mutex_downcall);
424         if (write(k5_fd, buf, p - buf) < p - buf) {
425                 lgssd_mutex_put(lgssd_mutex_downcall);
426                 goto out_err;
427         }
428         lgssd_mutex_put(lgssd_mutex_downcall);
429         return 0;
430 out_err:
431         printerr(0, "Failed to write error downcall!\n");
432         return -1;
433 }
434
435 static
436 int do_negotiation(struct lustre_gss_data *lgd,
437                    gss_buffer_desc *gss_token,
438                    struct lustre_gss_init_res *gr,
439                    int timeout)
440 {
441         struct lgssd_ioctl_param param;
442         struct passwd *pw;
443         char outbuf[8192];
444         unsigned int *p;
445         glob_t path;
446         int fd;
447         int rc;
448
449         pw = getpwuid(lgd->lgd_uid);
450         if (!pw) {
451                 printerr(0, "no uid %u in local user database\n",
452                          lgd->lgd_uid);
453                 return -1;
454         }
455
456         param.version = GSSD_INTERFACE_VERSION_V1;
457         param.uuid = lgd->lgd_uuid;
458         param.lustre_svc = lgd->lgd_lustre_svc;
459         param.uid = lgd->lgd_uid;
460         param.gid = pw->pw_gid;
461         param.send_token_size = gss_token->length;
462         param.send_token = (char *) gss_token->value;
463         param.reply_buf_size = sizeof(outbuf);
464         param.reply_buf = outbuf;
465
466         if (cfs_get_param_paths(&path, "sptlrpc/gss/init_channel") != 0)
467                 return -1;
468
469         fd = open(path.gl_pathv[0], O_RDWR);
470         if (fd < 0) {
471                 printerr(0, "can't open file %s\n", path.gl_pathv[0]);
472                 rc = -1;
473                 goto out_params;
474         }
475
476         rc = write(fd, &param, sizeof(param));
477         if (rc != sizeof(param)) {
478                 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
479                 rc = -1;
480                 goto out_fd;
481         }
482         if (param.status) {
483                 printerr(0, "status: %d (%s)\n",
484                          param.status, strerror((int)param.status));
485                 if (param.status == -ETIMEDOUT) {
486                         /* kernel return -ETIMEDOUT means the rpc timedout,
487                          * we should notify the caller to reinitiate the
488                          * gss negotiation, by return -ERESTART
489                          */
490                         lgd->lgd_rpc_err = -ERESTART;
491                         lgd->lgd_gss_err = 0;
492                 } else {
493                         lgd->lgd_rpc_err = param.status;
494                         lgd->lgd_gss_err = 0;
495                 }
496                 rc = -1;
497                 goto out_fd;
498         }
499         p = (unsigned int *)outbuf;
500         gr->gr_major = *p++;
501         gr->gr_minor = *p++;
502         gr->gr_win = *p++;
503
504         gr->gr_ctx.length = *p++;
505         gr->gr_ctx.value = malloc(gr->gr_ctx.length);
506         memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
507         p += (((gr->gr_ctx.length + 3) & ~3) / 4);
508
509         gr->gr_token.length = *p++;
510         gr->gr_token.value = malloc(gr->gr_token.length);
511         memcpy(gr->gr_token.value, p, gr->gr_token.length);
512         p += (((gr->gr_token.length + 3) & ~3) / 4);
513
514         printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
515                  gr->gr_ctx.length, gr->gr_token.length);
516         rc = 0;
517 out_fd:
518         close(fd);
519 out_params:
520         cfs_free_param_data(&path);
521         return rc;
522 }
523
524 static
525 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
526 {
527         struct lustre_gss_init_res gr;
528         gss_buffer_desc         *recv_tokenp, send_token;
529         OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
530
531         /* GSS context establishment loop. */
532         memset(&gr, 0, sizeof(gr));
533         recv_tokenp = GSS_C_NO_BUFFER;
534
535         for (;;) {
536                 /* print the token we just received */
537                 if (recv_tokenp != GSS_C_NO_BUFFER) {
538                         printerr(3, "The received token length %d\n",
539                                  recv_tokenp->length);
540                         print_hexl(3, recv_tokenp->value, recv_tokenp->length);
541                 }
542
543                 maj_stat = gss_init_sec_context(&min_stat,
544                                                 lgd->lgd_cred,
545                                                 &lgd->lgd_ctx,
546                                                 lgd->lgd_name,
547                                                 lgd->lgd_mech,
548                                                 lgd->lgd_req_flags,
549                                                 0,              /* time req */
550                                                 NULL,           /* channel */
551                                                 recv_tokenp,
552                                                 NULL,           /* used mech */
553                                                 &send_token,
554                                                 &ret_flags,
555                                                 NULL);          /* time rec */
556
557                 if (recv_tokenp != GSS_C_NO_BUFFER) {
558                         gss_release_buffer(&min_stat, &gr.gr_token);
559                         recv_tokenp = GSS_C_NO_BUFFER;
560                 }
561                 if (maj_stat != GSS_S_COMPLETE &&
562                     maj_stat != GSS_S_CONTINUE_NEEDED) {
563                         pgsserr("gss_init_sec_context", maj_stat, min_stat,
564                                 lgd->lgd_mech);
565                         break;
566                 }
567                 if (send_token.length != 0) {
568                         memset(&gr, 0, sizeof(gr));
569
570                         /* print the token we are about to send */
571                         printerr(3, "token being sent length %d\n",
572                                  send_token.length);
573                         print_hexl(3, send_token.value, send_token.length);
574
575                         call_stat = do_negotiation(lgd, &send_token, &gr, 0);
576                         gss_release_buffer(&min_stat, &send_token);
577
578                         if (call_stat != 0 ||
579                             (gr.gr_major != GSS_S_COMPLETE &&
580                              gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
581                                 printerr(0, "call stat %d, major stat 0x%x\n",
582                                          (int)call_stat, gr.gr_major);
583                                 return -1;
584                         }
585
586                         if (gr.gr_ctx.length != 0) {
587                                 if (lgd->lgd_rmt_ctx.value)
588                                         gss_release_buffer(&min_stat,
589                                                            &lgd->lgd_rmt_ctx);
590                                 lgd->lgd_rmt_ctx = gr.gr_ctx;
591                         }
592                         if (gr.gr_token.length != 0) {
593                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
594                                         break;
595                                 recv_tokenp = &gr.gr_token;
596                         }
597                 }
598
599                 /* GSS_S_COMPLETE => check gss header verifier,
600                  * usually checked in gss_validate
601                  */
602                 if (maj_stat == GSS_S_COMPLETE) {
603                         lgd->lgd_established = 1;
604                         lgd->lgd_seq_win = gr.gr_win;
605                         break;
606                 }
607         }
608         /* End context negotiation loop. */
609         if (!lgd->lgd_established) {
610                 if (gr.gr_token.length != 0)
611                         gss_release_buffer(&min_stat, &gr.gr_token);
612
613                 printerr(0, "context negotiation failed\n");
614                 return -1;
615         }
616
617         printerr(2, "successfully refreshed lgd\n");
618         return 0;
619 }
620
621 static
622 int gssd_create_lgd(struct clnt_info *clp,
623                     struct lustre_gss_data *lgd,
624                     struct lgssd_upcall_data *updata,
625                     int authtype)
626 {
627         gss_buffer_desc         sname;
628         OM_uint32               maj_stat, min_stat;
629         int                     retval = -1;
630
631         lgd->lgd_established = 0;
632         lgd->lgd_lustre_svc = updata->svc;
633         lgd->lgd_uid = updata->uid;
634         lgd->lgd_uuid = updata->obd;
635
636         switch (authtype) {
637         case AUTHTYPE_KRB5:
638                 lgd->lgd_mech = (gss_OID) &krb5oid;
639                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
640                 break;
641         case AUTHTYPE_SPKM3:
642                 lgd->lgd_mech = (gss_OID) &spkm3oid;
643                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
644                  * Need a way to switch....
645                  */
646                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
647                 break;
648         default:
649                 printerr(0, "Invalid authentication type (%d)\n", authtype);
650                 return -1;
651         }
652
653         lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
654         lgd->lgd_ctx = GSS_C_NO_CONTEXT;
655         lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
656         lgd->lgd_seq_win = 0;
657
658         sname.value = clp->servicename;
659         sname.length = strlen(clp->servicename);
660
661         maj_stat = gss_import_name(&min_stat, &sname,
662                                    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
663                                    &lgd->lgd_name);
664         if (maj_stat != GSS_S_COMPLETE) {
665                 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
666                 goto out_fail;
667         }
668
669         retval = gssd_refresh_lgd(lgd);
670
671         if (lgd->lgd_name != GSS_C_NO_NAME)
672                 gss_release_name(&min_stat, &lgd->lgd_name);
673
674         if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
675                 gss_release_cred(&min_stat, &lgd->lgd_cred);
676
677   out_fail:
678         return retval;
679 }
680
681 static
682 void gssd_free_lgd(struct lustre_gss_data *lgd)
683 {
684         gss_buffer_t            token = GSS_C_NO_BUFFER;
685         OM_uint32               maj_stat, min_stat;
686
687         if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
688                 return;
689
690         maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
691 }
692
693 static
694 int construct_service_name(struct clnt_info *clp,
695                            struct lgssd_upcall_data *ud)
696 {
697         const int buflen = 256;
698         char name[buflen];
699
700         if (clp->servicename) {
701                 free(clp->servicename);
702                 clp->servicename = NULL;
703         }
704
705         if (lnet_nid2hostname(ud->nid, name, buflen))
706                 return -1;
707
708         clp->servicename = malloc(32 + strlen(name));
709         if (!clp->servicename) {
710                 printerr(0, "can't alloc memory\n");
711                 return -1;
712         }
713         sprintf(clp->servicename, "%s@%s",
714                 ud->svc == LUSTRE_GSS_SVC_MDS ?
715                 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
716                 name);
717         printerr(2, "constructed servicename: %s\n", clp->servicename);
718         return 0;
719 }
720
721 /*
722  * this code uses the userland rpcsec gss library to create a krb5
723  * context on behalf of the kernel
724  */
725 void
726 handle_krb5_upcall(struct clnt_info *clp)
727 {
728         pid_t                   pid;
729         gss_buffer_desc         token = { 0, NULL };
730         struct lgssd_upcall_data updata;
731         struct lustre_gss_data  lgd;
732         char                    **credlist = NULL;
733         char                    **ccname;
734         int                     read_rc;
735
736         printerr(2, "handling krb5 upcall\n");
737
738         memset(&lgd, 0, sizeof(lgd));
739         lgd.lgd_rpc_err = -EPERM; /* default error code */
740
741         read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
742         if (read_rc < 0) {
743                 printerr(0, "WARNING: failed reading from krb5 "
744                             "upcall pipe: %s\n", strerror(errno));
745                 return;
746         } else if (read_rc != sizeof(updata)) {
747                 printerr(0, "upcall data mismatch: length %d, expect %d\n",
748                          read_rc, sizeof(updata));
749
750                 /* the sequence number must be the first field. if read >= 4
751                  * bytes then we know at least sequence is fine, try to send
752                  * error notification nicely.
753                  */
754                 if (read_rc >= 4)
755                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
756                 return;
757         }
758
759         /* FIXME temporary fix, do this before fork.
760          * in case of errors could have memory leak!!!
761          */
762         if (updata.uid == 0) {
763                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
764                         printerr(0, "ERROR: Failed to obtain machine "
765                                     "credentials\n");
766                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
767                         return;
768                 }
769         }
770
771         /* fork child process */
772         pid = fork();
773         if (pid < 0) {
774                 printerr(0, "can't fork: %s\n", strerror(errno));
775                 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
776                 return;
777         } else if (pid > 0) {
778                 printerr(2, "forked child process: %d\n", pid);
779                 return;
780         }
781
782         printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
783                  updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
784
785         if (updata.svc != LUSTRE_GSS_SVC_MDS &&
786             updata.svc != LUSTRE_GSS_SVC_OSS) {
787                 printerr(0, "invalid svc %d\n", updata.svc);
788                 lgd.lgd_rpc_err = -EPROTO;
789                 goto out_return_error;
790         }
791         updata.obd[sizeof(updata.obd)-1] = '\0';
792
793         if (construct_service_name(clp, &updata)) {
794                 printerr(0, "failed to construct service name\n");
795                 goto out_return_error;
796         }
797
798         if (updata.uid == 0) {
799                 int success = 0;
800
801                 /*
802                  * Get a list of credential cache names and try each
803                  * of them until one works or we've tried them all
804                  */
805 /*
806                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
807                         printerr(0, "ERROR: Failed to obtain machine "
808                                     "credentials for %s\n", clp->servicename);
809                         goto out_return_error;
810                 }
811 */
812                 for (ccname = credlist; ccname && *ccname; ccname++) {
813                         gssd_setup_krb5_machine_gss_ccache(*ccname);
814                         if ((gssd_create_lgd(clp, &lgd, &updata,
815                                              AUTHTYPE_KRB5)) == 0) {
816                                 /* Success! */
817                                 success++;
818                                 break;
819                         }
820                         printerr(2, "WARNING: Failed to create krb5 context "
821                                     "for user with uid %d with credentials "
822                                     "cache %s for service %s\n",
823                                  updata.uid, *ccname, clp->servicename);
824                 }
825                 gssd_free_krb5_machine_cred_list(credlist);
826                 if (!success) {
827                         printerr(0, "ERROR: Failed to create krb5 context "
828                                     "for user with uid %d with any "
829                                     "credentials cache for service %s\n",
830                                  updata.uid, clp->servicename);
831                         goto out_return_error;
832                 }
833         }
834         else {
835                 /* Tell krb5 gss which credentials cache to use */
836                 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
837
838                 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
839                         printerr(0, "WARNING: Failed to create krb5 context "
840                                     "for user with uid %d for service %s\n",
841                                  updata.uid, clp->servicename);
842                         goto out_return_error;
843                 }
844         }
845
846         if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
847                 printerr(0, "WARNING: Failed to serialize krb5 context for "
848                             "user with uid %d for service %s\n",
849                          updata.uid, clp->servicename);
850                 goto out_return_error;
851         }
852
853         printerr(1, "refreshed: %u@%s for %s\n",
854                  updata.uid, updata.obd, clp->servicename);
855         do_downcall(clp->krb5_fd, &updata, &lgd, &token);
856
857 out:
858         if (token.value)
859                 free(token.value);
860
861         gssd_free_lgd(&lgd);
862         exit(0); /* i'm child process */
863
864 out_return_error:
865         do_error_downcall(clp->krb5_fd, updata.seq,
866                           lgd.lgd_rpc_err, lgd.lgd_gss_err);
867         goto out;
868 }
869