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