Whamcloud - gitweb
LU-3289 gss: Interface and code changes for shared key
[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 #if 0
437 /*
438  * Create an RPC connection and establish an authenticated
439  * gss context with a server.
440  */
441 int create_auth_rpc_client(struct clnt_info *clp,
442                            CLIENT **clnt_return,
443                            AUTH **auth_return,
444                            uid_t uid,
445                            int authtype)
446 {
447         CLIENT                  *rpc_clnt = NULL;
448         struct rpc_gss_sec      sec;
449         AUTH                    *auth = NULL;
450         uid_t                   save_uid = -1;
451         int                     retval = -1;
452         int                     errcode;
453         OM_uint32               min_stat;
454         char                    rpc_errmsg[1024];
455         int                     sockp = RPC_ANYSOCK;
456         int                     sendsz = 32768, recvsz = 32768;
457         struct addrinfo         ai_hints, *a = NULL;
458         char                    service[64];
459         char                    *at_sign;
460
461         /* Create the context as the user (not as root) */
462         save_uid = geteuid();
463         if (setfsuid(uid) != 0) {
464                 printerr(0, "WARNING: Failed to setfsuid for "
465                             "user with uid %d\n", uid);
466                 goto out_fail;
467         }
468         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
469                         uid, save_uid);
470
471         sec.qop = GSS_C_QOP_DEFAULT;
472         sec.svc = RPCSEC_GSS_SVC_NONE;
473         sec.cred = GSS_C_NO_CREDENTIAL;
474         sec.req_flags = 0;
475         if (authtype == AUTHTYPE_KRB5) {
476                 sec.mech = (gss_OID)&krb5oid;
477                 sec.req_flags = GSS_C_MUTUAL_FLAG;
478         }
479         else if (authtype == AUTHTYPE_SPKM3) {
480                 sec.mech = (gss_OID)&spkm3oid;
481                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
482                  * Need a way to switch....
483                  */
484                 sec.req_flags = GSS_C_MUTUAL_FLAG;
485         }
486         else {
487                 printerr(0, "ERROR: Invalid authentication type (%d) "
488                         "in create_auth_rpc_client\n", authtype);
489                 goto out_fail;
490         }
491
492
493         if (authtype == AUTHTYPE_KRB5) {
494 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
495                 /*
496                  * Do this before creating rpc connection since we won't need
497                  * rpc connection if it fails!
498                  */
499                 if (limit_krb5_enctypes(&sec, uid)) {
500                         printerr(1, "WARNING: Failed while limiting krb5 "
501                                     "encryption types for user with uid %d\n",
502                                  uid);
503                         goto out_fail;
504                 }
505 #endif
506         }
507
508         /* create an rpc connection to the nfs server */
509
510         printerr(2, "creating %s client for server %s\n", clp->protocol,
511                         clp->servername);
512
513         memset(&ai_hints, '\0', sizeof(ai_hints));
514         ai_hints.ai_family = PF_INET;
515         ai_hints.ai_flags |= AI_CANONNAME;
516         if ((strcmp(clp->protocol, "tcp")) == 0) {
517                 ai_hints.ai_socktype = SOCK_STREAM;
518                 ai_hints.ai_protocol = IPPROTO_TCP;
519         } else if ((strcmp(clp->protocol, "udp")) == 0) {
520                 ai_hints.ai_socktype = SOCK_DGRAM;
521                 ai_hints.ai_protocol = IPPROTO_UDP;
522         } else {
523                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
524                          "for connection to server %s for user with uid %d",
525                          clp->protocol, clp->servername, uid);
526                 goto out_fail;
527         }
528
529         /* extract the service name from clp->servicename */
530         if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
531                 printerr(0, "WARNING: servicename (%s) not formatted as "
532                         "expected with service@host", clp->servicename);
533                 goto out_fail;
534         }
535         if ((at_sign - clp->servicename) >= sizeof(service)) {
536                 printerr(0, "WARNING: service portion of servicename (%s) "
537                         "is too long!", clp->servicename);
538                 goto out_fail;
539         }
540         strncpy(service, clp->servicename, at_sign - clp->servicename);
541         service[at_sign - clp->servicename] = '\0';
542
543         errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
544         if (errcode) {
545                 printerr(0, "WARNING: Error from getaddrinfo for server "
546                          "'%s': %s", clp->servername, gai_strerror(errcode));
547                 goto out_fail;
548         }
549
550         if (a == NULL) {
551                 printerr(0, "WARNING: No address information found for "
552                          "connection to server %s for user with uid %d",
553                          clp->servername, uid);
554                 goto out_fail;
555         }
556         if (a->ai_protocol == IPPROTO_TCP) {
557                 if ((rpc_clnt = clnttcp_create(
558                                         (struct sockaddr_in *) a->ai_addr,
559                                         clp->prog, clp->vers, &sockp,
560                                         sendsz, recvsz)) == NULL) {
561                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
562                                  "WARNING: can't create tcp rpc_clnt "
563                                  "for server %s for user with uid %d",
564                                  clp->servername, uid);
565                         printerr(0, "%s\n",
566                                  clnt_spcreateerror(rpc_errmsg));
567                         goto out_fail;
568                 }
569         } else if (a->ai_protocol == IPPROTO_UDP) {
570                 const struct timeval timeout = {5, 0};
571                 if ((rpc_clnt = clntudp_bufcreate(
572                                         (struct sockaddr_in *) a->ai_addr,
573                                         clp->prog, clp->vers, timeout,
574                                         &sockp, sendsz, recvsz)) == NULL) {
575                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
576                                  "WARNING: can't create udp rpc_clnt "
577                                  "for server %s for user with uid %d",
578                                  clp->servername, uid);
579                         printerr(0, "%s\n",
580                                  clnt_spcreateerror(rpc_errmsg));
581                         goto out_fail;
582                 }
583         } else {
584                 /* Shouldn't happen! */
585                 printerr(0, "ERROR: requested protocol '%s', but "
586                          "got addrinfo with protocol %d",
587                          clp->protocol, a->ai_protocol);
588                 goto out_fail;
589         }
590         /* We're done with this */
591         freeaddrinfo(a);
592         a = NULL;
593
594         printerr(2, "creating context with server %s\n", clp->servicename);
595         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
596         if (!auth) {
597                 /* Our caller should print appropriate message */
598                 printerr(2, "WARNING: Failed to create %s context for "
599                             "user with uid %d for server %s\n",
600                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
601                          uid, clp->servername);
602                 goto out_fail;
603         }
604
605         /* Success !!! */
606         rpc_clnt->cl_auth = auth;
607         *clnt_return = rpc_clnt;
608         *auth_return = auth;
609         retval = 0;
610
611   out:
612         if (sec.cred != GSS_C_NO_CREDENTIAL)
613                 gss_release_cred(&min_stat, &sec.cred);
614         if (a != NULL) freeaddrinfo(a);
615         /* Restore euid to original value */
616         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
617                 printerr(0, "WARNING: Failed to restore fsuid"
618                             " to uid %d from %d\n", save_uid, uid);
619         }
620         return retval;
621
622   out_fail:
623         /* Only destroy here if failure.  Otherwise, caller is responsible */
624         if (rpc_clnt) clnt_destroy(rpc_clnt);
625
626         goto out;
627 }
628 #endif
629
630 static
631 int do_negotiation(struct lustre_gss_data *lgd,
632                    gss_buffer_desc *gss_token,
633                    struct lustre_gss_init_res *gr,
634                    int timeout)
635 {
636         struct lgssd_ioctl_param param;
637         struct passwd *pw;
638         char outbuf[8192];
639         unsigned int *p;
640         glob_t path;
641         int fd;
642         int rc;
643
644         pw = getpwuid(lgd->lgd_uid);
645         if (!pw) {
646                 printerr(0, "no uid %u in local user database\n",
647                          lgd->lgd_uid);
648                 return -1;
649         }
650
651         param.version = GSSD_INTERFACE_VERSION_V1;
652         param.uuid = lgd->lgd_uuid;
653         param.lustre_svc = lgd->lgd_lustre_svc;
654         param.uid = lgd->lgd_uid;
655         param.gid = pw->pw_gid;
656         param.send_token_size = gss_token->length;
657         param.send_token = (char *) gss_token->value;
658         param.reply_buf_size = sizeof(outbuf);
659         param.reply_buf = outbuf;
660
661         if (cfs_get_param_paths(&path, "sptlrpc/gss/init_channel") != 0)
662                 return -1;
663
664         fd = open(path.gl_pathv[0], O_RDWR);
665         if (fd < 0) {
666                 printerr(0, "can't open file %s\n", path.gl_pathv[0]);
667                 rc = -1;
668                 goto out_params;
669         }
670
671         rc = write(fd, &param, sizeof(param));
672         if (rc != sizeof(param)) {
673                 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
674                 rc = -1;
675                 goto out_fd;
676         }
677         if (param.status) {
678                 printerr(0, "status: %d (%s)\n",
679                          param.status, strerror((int)param.status));
680                 if (param.status == -ETIMEDOUT) {
681                         /* kernel return -ETIMEDOUT means the rpc timedout,
682                          * we should notify the caller to reinitiate the
683                          * gss negotiation, by return -ERESTART
684                          */
685                         lgd->lgd_rpc_err = -ERESTART;
686                         lgd->lgd_gss_err = 0;
687                 } else {
688                         lgd->lgd_rpc_err = param.status;
689                         lgd->lgd_gss_err = 0;
690                 }
691                 rc = -1;
692                 goto out_fd;
693         }
694         p = (unsigned int *)outbuf;
695         gr->gr_major = *p++;
696         gr->gr_minor = *p++;
697         gr->gr_win = *p++;
698
699         gr->gr_ctx.length = *p++;
700         gr->gr_ctx.value = malloc(gr->gr_ctx.length);
701         memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
702         p += (((gr->gr_ctx.length + 3) & ~3) / 4);
703
704         gr->gr_token.length = *p++;
705         gr->gr_token.value = malloc(gr->gr_token.length);
706         memcpy(gr->gr_token.value, p, gr->gr_token.length);
707         p += (((gr->gr_token.length + 3) & ~3) / 4);
708
709         printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
710                  gr->gr_ctx.length, gr->gr_token.length);
711         rc = 0;
712 out_fd:
713         close(fd);
714 out_params:
715         cfs_free_param_data(&path);
716         return rc;
717 }
718
719 static
720 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
721 {
722         struct lustre_gss_init_res gr;
723         gss_buffer_desc         *recv_tokenp, send_token;
724         OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
725
726         /* GSS context establishment loop. */
727         memset(&gr, 0, sizeof(gr));
728         recv_tokenp = GSS_C_NO_BUFFER;
729
730         for (;;) {
731                 /* print the token we just received */
732                 if (recv_tokenp != GSS_C_NO_BUFFER) {
733                         printerr(3, "The received token length %d\n",
734                                  recv_tokenp->length);
735                         print_hexl(3, recv_tokenp->value, recv_tokenp->length);
736                 }
737
738                 maj_stat = gss_init_sec_context(&min_stat,
739                                                 lgd->lgd_cred,
740                                                 &lgd->lgd_ctx,
741                                                 lgd->lgd_name,
742                                                 lgd->lgd_mech,
743                                                 lgd->lgd_req_flags,
744                                                 0,              /* time req */
745                                                 NULL,           /* channel */
746                                                 recv_tokenp,
747                                                 NULL,           /* used mech */
748                                                 &send_token,
749                                                 &ret_flags,
750                                                 NULL);          /* time rec */
751
752                 if (recv_tokenp != GSS_C_NO_BUFFER) {
753                         gss_release_buffer(&min_stat, &gr.gr_token);
754                         recv_tokenp = GSS_C_NO_BUFFER;
755                 }
756                 if (maj_stat != GSS_S_COMPLETE &&
757                     maj_stat != GSS_S_CONTINUE_NEEDED) {
758                         pgsserr("gss_init_sec_context", maj_stat, min_stat,
759                                 lgd->lgd_mech);
760                         break;
761                 }
762                 if (send_token.length != 0) {
763                         memset(&gr, 0, sizeof(gr));
764
765                         /* print the token we are about to send */
766                         printerr(3, "token being sent length %d\n",
767                                  send_token.length);
768                         print_hexl(3, send_token.value, send_token.length);
769
770                         call_stat = do_negotiation(lgd, &send_token, &gr, 0);
771                         gss_release_buffer(&min_stat, &send_token);
772
773                         if (call_stat != 0 ||
774                             (gr.gr_major != GSS_S_COMPLETE &&
775                              gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
776                                 printerr(0, "call stat %d, major stat 0x%x\n",
777                                          (int)call_stat, gr.gr_major);
778                                 return -1;
779                         }
780
781                         if (gr.gr_ctx.length != 0) {
782                                 if (lgd->lgd_rmt_ctx.value)
783                                         gss_release_buffer(&min_stat,
784                                                            &lgd->lgd_rmt_ctx);
785                                 lgd->lgd_rmt_ctx = gr.gr_ctx;
786                         }
787                         if (gr.gr_token.length != 0) {
788                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
789                                         break;
790                                 recv_tokenp = &gr.gr_token;
791                         }
792                 }
793
794                 /* GSS_S_COMPLETE => check gss header verifier,
795                  * usually checked in gss_validate
796                  */
797                 if (maj_stat == GSS_S_COMPLETE) {
798                         lgd->lgd_established = 1;
799                         lgd->lgd_seq_win = gr.gr_win;
800                         break;
801                 }
802         }
803         /* End context negotiation loop. */
804         if (!lgd->lgd_established) {
805                 if (gr.gr_token.length != 0)
806                         gss_release_buffer(&min_stat, &gr.gr_token);
807
808                 printerr(0, "context negotiation failed\n");
809                 return -1;
810         }
811
812         printerr(2, "successfully refreshed lgd\n");
813         return 0;
814 }
815
816 static
817 int gssd_create_lgd(struct clnt_info *clp,
818                     struct lustre_gss_data *lgd,
819                     struct lgssd_upcall_data *updata,
820                     int authtype)
821 {
822         gss_buffer_desc         sname;
823         OM_uint32               maj_stat, min_stat;
824         int                     retval = -1;
825
826         lgd->lgd_established = 0;
827         lgd->lgd_lustre_svc = updata->svc;
828         lgd->lgd_uid = updata->uid;
829         lgd->lgd_uuid = updata->obd;
830
831         switch (authtype) {
832         case AUTHTYPE_KRB5:
833                 lgd->lgd_mech = (gss_OID) &krb5oid;
834                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
835                 break;
836         case AUTHTYPE_SPKM3:
837                 lgd->lgd_mech = (gss_OID) &spkm3oid;
838                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
839                  * Need a way to switch....
840                  */
841                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
842                 break;
843         default:
844                 printerr(0, "Invalid authentication type (%d)\n", authtype);
845                 return -1;
846         }
847
848         lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
849         lgd->lgd_ctx = GSS_C_NO_CONTEXT;
850         lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
851         lgd->lgd_seq_win = 0;
852
853         sname.value = clp->servicename;
854         sname.length = strlen(clp->servicename);
855
856         maj_stat = gss_import_name(&min_stat, &sname,
857                                    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
858                                    &lgd->lgd_name);
859         if (maj_stat != GSS_S_COMPLETE) {
860                 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
861                 goto out_fail;
862         }
863
864         retval = gssd_refresh_lgd(lgd);
865
866         if (lgd->lgd_name != GSS_C_NO_NAME)
867                 gss_release_name(&min_stat, &lgd->lgd_name);
868
869         if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
870                 gss_release_cred(&min_stat, &lgd->lgd_cred);
871
872   out_fail:
873         return retval;
874 }
875
876 static
877 void gssd_free_lgd(struct lustre_gss_data *lgd)
878 {
879         gss_buffer_t            token = GSS_C_NO_BUFFER;
880         OM_uint32               maj_stat, min_stat;
881
882         if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
883                 return;
884
885         maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
886 }
887
888 static
889 int construct_service_name(struct clnt_info *clp,
890                            struct lgssd_upcall_data *ud)
891 {
892         const int buflen = 256;
893         char name[buflen];
894
895         if (clp->servicename) {
896                 free(clp->servicename);
897                 clp->servicename = NULL;
898         }
899
900         if (lnet_nid2hostname(ud->nid, name, buflen))
901                 return -1;
902
903         clp->servicename = malloc(32 + strlen(name));
904         if (!clp->servicename) {
905                 printerr(0, "can't alloc memory\n");
906                 return -1;
907         }
908         sprintf(clp->servicename, "%s@%s",
909                 ud->svc == LUSTRE_GSS_SVC_MDS ?
910                 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
911                 name);
912         printerr(2, "constructed servicename: %s\n", clp->servicename);
913         return 0;
914 }
915
916 /*
917  * this code uses the userland rpcsec gss library to create a krb5
918  * context on behalf of the kernel
919  */
920 void
921 handle_krb5_upcall(struct clnt_info *clp)
922 {
923         pid_t                   pid;
924         gss_buffer_desc         token = { 0, NULL };
925         struct lgssd_upcall_data updata;
926         struct lustre_gss_data  lgd;
927         char                    **credlist = NULL;
928         char                    **ccname;
929         int                     read_rc;
930
931         printerr(2, "handling krb5 upcall\n");
932
933         memset(&lgd, 0, sizeof(lgd));
934         lgd.lgd_rpc_err = -EPERM; /* default error code */
935
936         read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
937         if (read_rc < 0) {
938                 printerr(0, "WARNING: failed reading from krb5 "
939                             "upcall pipe: %s\n", strerror(errno));
940                 return;
941         } else if (read_rc != sizeof(updata)) {
942                 printerr(0, "upcall data mismatch: length %d, expect %d\n",
943                          read_rc, sizeof(updata));
944
945                 /* the sequence number must be the first field. if read >= 4
946                  * bytes then we know at least sequence is fine, try to send
947                  * error notification nicely.
948                  */
949                 if (read_rc >= 4)
950                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
951                 return;
952         }
953
954         /* FIXME temporary fix, do this before fork.
955          * in case of errors could have memory leak!!!
956          */
957         if (updata.uid == 0) {
958                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
959                         printerr(0, "ERROR: Failed to obtain machine "
960                                     "credentials\n");
961                         do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
962                         return;
963                 }
964         }
965
966         /* fork child process */
967         pid = fork();
968         if (pid < 0) {
969                 printerr(0, "can't fork: %s\n", strerror(errno));
970                 do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
971                 return;
972         } else if (pid > 0) {
973                 printerr(2, "forked child process: %d\n", pid);
974                 return;
975         }
976
977         printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
978                  updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
979
980         if (updata.svc != LUSTRE_GSS_SVC_MDS &&
981             updata.svc != LUSTRE_GSS_SVC_OSS) {
982                 printerr(0, "invalid svc %d\n", updata.svc);
983                 lgd.lgd_rpc_err = -EPROTO;
984                 goto out_return_error;
985         }
986         updata.obd[sizeof(updata.obd)-1] = '\0';
987
988         if (construct_service_name(clp, &updata)) {
989                 printerr(0, "failed to construct service name\n");
990                 goto out_return_error;
991         }
992
993         if (updata.uid == 0) {
994                 int success = 0;
995
996                 /*
997                  * Get a list of credential cache names and try each
998                  * of them until one works or we've tried them all
999                  */
1000 /*
1001                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
1002                         printerr(0, "ERROR: Failed to obtain machine "
1003                                     "credentials for %s\n", clp->servicename);
1004                         goto out_return_error;
1005                 }
1006 */
1007                 for (ccname = credlist; ccname && *ccname; ccname++) {
1008                         gssd_setup_krb5_machine_gss_ccache(*ccname);
1009                         if ((gssd_create_lgd(clp, &lgd, &updata,
1010                                              AUTHTYPE_KRB5)) == 0) {
1011                                 /* Success! */
1012                                 success++;
1013                                 break;
1014                         }
1015                         printerr(2, "WARNING: Failed to create krb5 context "
1016                                     "for user with uid %d with credentials "
1017                                     "cache %s for service %s\n",
1018                                  updata.uid, *ccname, clp->servicename);
1019                 }
1020                 gssd_free_krb5_machine_cred_list(credlist);
1021                 if (!success) {
1022                         printerr(0, "ERROR: Failed to create krb5 context "
1023                                     "for user with uid %d with any "
1024                                     "credentials cache for service %s\n",
1025                                  updata.uid, clp->servicename);
1026                         goto out_return_error;
1027                 }
1028         }
1029         else {
1030                 /* Tell krb5 gss which credentials cache to use */
1031                 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
1032
1033                 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
1034                         printerr(0, "WARNING: Failed to create krb5 context "
1035                                     "for user with uid %d for service %s\n",
1036                                  updata.uid, clp->servicename);
1037                         goto out_return_error;
1038                 }
1039         }
1040
1041         if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
1042                 printerr(0, "WARNING: Failed to serialize krb5 context for "
1043                             "user with uid %d for service %s\n",
1044                          updata.uid, clp->servicename);
1045                 goto out_return_error;
1046         }
1047
1048         printerr(1, "refreshed: %u@%s for %s\n",
1049                  updata.uid, updata.obd, clp->servicename);
1050         do_downcall(clp->krb5_fd, &updata, &lgd, &token);
1051
1052 out:
1053         if (token.value)
1054                 free(token.value);
1055
1056         gssd_free_lgd(&lgd);
1057         exit(0); /* i'm child process */
1058
1059 out_return_error:
1060         do_error_downcall(clp->krb5_fd, updata.seq,
1061                           lgd.lgd_rpc_err, lgd.lgd_gss_err);
1062         goto out;
1063 }
1064
1065 /*
1066  * this code uses the userland rpcsec gss library to create an spkm3
1067  * context on behalf of the kernel
1068  */
1069 void
1070 handle_spkm3_upcall(struct clnt_info *clp)
1071 {
1072 #if 0
1073         uid_t                   uid;
1074         CLIENT                  *rpc_clnt = NULL;
1075         AUTH                    *auth = NULL;
1076         struct authgss_private_data pd;
1077         gss_buffer_desc         token;
1078
1079         printerr(2, "handling spkm3 upcall\n");
1080
1081         token.length = 0;
1082         token.value = NULL;
1083
1084         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1085                 printerr(0, "WARNING: failed reading uid from spkm3 "
1086                          "upcall pipe: %s\n", strerror(errno));
1087                 goto out;
1088         }
1089
1090         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1091                 printerr(0, "WARNING: Failed to create spkm3 context for "
1092                             "user with uid %d\n", uid);
1093                 goto out_return_error;
1094         }
1095
1096         if (!authgss_get_private_data(auth, &pd)) {
1097                 printerr(0, "WARNING: Failed to obtain authentication "
1098                             "data for user with uid %d for server %s\n",
1099                          uid, clp->servername);
1100                 goto out_return_error;
1101         }
1102
1103         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1104                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1105                             "user with uid %d for server\n",
1106                          uid, clp->servername);
1107                 goto out_return_error;
1108         }
1109
1110         do_downcall(clp->spkm3_fd, uid, &pd, &token);
1111
1112 out:
1113         if (token.value)
1114                 free(token.value);
1115         if (auth)
1116                 AUTH_DESTROY(auth);
1117         if (rpc_clnt)
1118                 clnt_destroy(rpc_clnt);
1119         return;
1120
1121 out_return_error:
1122         do_error_downcall(clp->spkm3_fd, uid, -1);
1123         goto out;
1124 #endif
1125 }