Whamcloud - gitweb
branch: b_new_cmd
[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
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <pwd.h>
53 #include <grp.h>
54 #include <string.h>
55 #include <dirent.h>
56 #include <poll.h>
57 #include <fcntl.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <gssapi/gssapi.h>
62 #include <netdb.h>
63
64 #include "gssd.h"
65 #include "err_util.h"
66 #include "gss_util.h"
67 #include "gss_oids.h"
68 #include "krb5_util.h"
69 #include "context.h"
70 #include "lsupport.h"
71
72 /*
73  * pollarray:
74  *      array of struct pollfd suitable to pass to poll. initialized to
75  *      zero - a zero struct is ignored by poll() because the events mask is 0.
76  *
77  * clnt_list:
78  *      linked list of struct clnt_info which associates a clntXXX directory
79  *      with an index into pollarray[], and other basic data about that client.
80  *
81  * Directory structure: created by the kernel nfs client
82  *      /pipefsdir/clntXX             : one per rpc_clnt struct in the kernel
83  *      /pipefsdir/clntXX/krb5        : read uid for which kernel wants
84  *                                       a context, write the resulting context
85  *      /pipefsdir/clntXX/info        : stores info such as server name
86  *
87  * Algorithm:
88  *      Poll all /pipefsdir/clntXX/krb5 files.  When ready, data read
89  *      is a uid; performs rpcsec_gss context initialization protocol to
90  *      get a cred for that user.  Writes result to corresponding krb5 file
91  *      in a form the kernel code will understand.
92  *      In addition, we make sure we are notified whenever anything is
93  *      created or destroyed in pipefsdir/ or in an of the clntXX directories,
94  *      and rescan the whole pipefsdir when this happens.
95  */
96
97 struct pollfd * pollarray;
98
99 int pollsize;  /* the size of pollaray (in pollfd's) */
100
101 static void
102 destroy_client(struct clnt_info *clp)
103 {
104         if (clp->krb5_poll_index != -1)
105                 memset(&pollarray[clp->krb5_poll_index], 0,
106                                         sizeof(struct pollfd));
107         if (clp->spkm3_poll_index != -1)
108                 memset(&pollarray[clp->spkm3_poll_index], 0,
109                                         sizeof(struct pollfd));
110         if (clp->dir_fd != -1) close(clp->dir_fd);
111         if (clp->krb5_fd != -1) close(clp->krb5_fd);
112         if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
113         if (clp->dirname) free(clp->dirname);
114         if (clp->servicename) free(clp->servicename);
115         free(clp);
116 }
117
118 static struct clnt_info *
119 insert_new_clnt(void)
120 {
121         struct clnt_info        *clp = NULL;
122
123         if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
124                 printerr(0, "ERROR: can't malloc clnt_info: %s\n",
125                          strerror(errno));
126                 goto out;
127         }
128         clp->krb5_poll_index = -1;
129         clp->spkm3_poll_index = -1;
130         clp->krb5_fd = -1;
131         clp->spkm3_fd = -1;
132         clp->dir_fd = -1;
133
134         TAILQ_INSERT_HEAD(&clnt_list, clp, list);
135 out:
136         return clp;
137 }
138
139 static int
140 process_clnt_dir_files(struct clnt_info * clp)
141 {
142         char    kname[32];
143         char    sname[32];
144
145         if (clp->krb5_fd == -1) {
146                 snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
147                 clp->krb5_fd = open(kname, O_RDWR);
148         }
149         if (clp->spkm3_fd == -1) {
150                 snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
151                 clp->spkm3_fd = open(sname, O_RDWR);
152         }
153         if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
154                 return -1;
155         return 0;
156 }
157
158 static int
159 get_poll_index(int *ind)
160 {
161         int i;
162
163         *ind = -1;
164         for (i=0; i<FD_ALLOC_BLOCK; i++) {
165                 if (pollarray[i].events == 0) {
166                         *ind = i;
167                         break;
168                 }
169         }
170         if (*ind == -1) {
171                 printerr(0, "ERROR: No pollarray slots open\n");
172                 return -1;
173         }
174         return 0;
175 }
176
177
178 static int
179 insert_clnt_poll(struct clnt_info *clp)
180 {
181         if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
182                 if (get_poll_index(&clp->krb5_poll_index)) {
183                         printerr(0, "ERROR: Too many krb5 clients\n");
184                         return -1;
185                 }
186                 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
187                 pollarray[clp->krb5_poll_index].events |= POLLIN;
188                 printerr(2, "monitoring krb5 channel under %s\n",
189                          clp->dirname);
190         }
191
192         if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
193                 if (get_poll_index(&clp->spkm3_poll_index)) {
194                         printerr(0, "ERROR: Too many spkm3 clients\n");
195                         return -1;
196                 }
197                 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
198                 pollarray[clp->spkm3_poll_index].events |= POLLIN;
199         }
200
201         return 0;
202 }
203
204 static void
205 process_clnt_dir(char *dir)
206 {
207         struct clnt_info *      clp;
208
209         if (!(clp = insert_new_clnt()))
210                 goto fail_destroy_client;
211
212         if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
213                 goto fail_destroy_client;
214         }
215         memcpy(clp->dirname, dir, strlen(dir));
216         if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
217                 printerr(0, "ERROR: can't open %s: %s\n",
218                          clp->dirname, strerror(errno));
219                 goto fail_destroy_client;
220         }
221         fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
222         fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
223
224         if (process_clnt_dir_files(clp))
225                 goto fail_keep_client;
226
227         if (insert_clnt_poll(clp))
228                 goto fail_destroy_client;
229
230         return;
231
232 fail_destroy_client:
233         if (clp) {
234                 TAILQ_REMOVE(&clnt_list, clp, list);
235                 destroy_client(clp);
236         }
237 fail_keep_client:
238         /* We couldn't find some subdirectories, but we keep the client
239          * around in case we get a notification on the directory when the
240          * subdirectories are created. */
241         return;
242 }
243
244 void
245 init_client_list(void)
246 {
247         TAILQ_INIT(&clnt_list);
248         /* Eventually plan to grow/shrink poll array: */
249         pollsize = FD_ALLOC_BLOCK;
250         pollarray = calloc(pollsize, sizeof(struct pollfd));
251 }
252
253 /*
254  * This is run after a DNOTIFY signal, and should clear up any
255  * directories that are no longer around, and re-scan any existing
256  * directories, since the DNOTIFY could have been in there.
257  */
258 static void
259 update_old_clients(struct dirent **namelist, int size)
260 {
261         struct clnt_info *clp;
262         void *saveprev;
263         int i, stillhere;
264
265         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
266                 stillhere = 0;
267                 for (i=0; i < size; i++) {
268                         if (!strcmp(clp->dirname, namelist[i]->d_name)) {
269                                 stillhere = 1;
270                                 break;
271                         }
272                 }
273                 if (!stillhere) {
274                         printerr(2, "destroying client %s\n", clp->dirname);
275                         saveprev = clp->list.tqe_prev;
276                         TAILQ_REMOVE(&clnt_list, clp, list);
277                         destroy_client(clp);
278                         clp = saveprev;
279                 }
280         }
281         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
282                 if (!process_clnt_dir_files(clp))
283                         insert_clnt_poll(clp);
284         }
285 }
286
287 /* Search for a client by directory name, return 1 if found, 0 otherwise */
288 static int
289 find_client(char *dirname)
290 {
291         struct clnt_info        *clp;
292
293         for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
294                 if (!strcmp(clp->dirname, dirname))
295                         return 1;
296         return 0;
297 }
298
299 /* Used to read (and re-read) list of clients, set up poll array. */
300 int
301 update_client_list(void)
302 {
303         char lustre_dir[PATH_MAX];
304         struct dirent lustre_dirent = { .d_name = "lustre" };
305         struct dirent *namelist[1];
306         struct stat statbuf;
307         int i, j;
308
309         if (chdir(pipefsdir) < 0) {
310                 printerr(0, "ERROR: can't chdir to %s: %s\n",
311                          pipefsdir, strerror(errno));
312                 return -1;
313         }
314
315         snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
316         if (stat(lustre_dir, &statbuf) == 0) {
317                 namelist[0] = &lustre_dirent;
318                 j = 1;
319                 printerr(2, "re-processing lustre directory\n");
320         } else {
321                 namelist[0] = NULL;
322                 j = 0;
323                 printerr(2, "lustre directory not exist\n");
324         }
325
326         update_old_clients(namelist, j);
327         for (i=0; i < j; i++) {
328                 if (i < FD_ALLOC_BLOCK &&
329                     !find_client(namelist[i]->d_name))
330                         process_clnt_dir(namelist[i]->d_name);
331         }
332
333         chdir("/");
334         return 0;
335 }
336
337 /* Context creation response. */
338 struct lustre_gss_init_res {
339         gss_buffer_desc gr_ctx;         /* context handle */
340         u_int           gr_major;       /* major status */
341         u_int           gr_minor;       /* minor status */
342         u_int           gr_win;         /* sequence window */
343         gss_buffer_desc gr_token;       /* token */
344 };
345
346 struct lustre_gss_data {
347         int             lgd_established;
348         int             lgd_lustre_svc; /* mds/oss */
349         int             lgd_uid;        /* uid */
350         char           *lgd_uuid;       /* client device uuid */
351         gss_name_t      lgd_name;       /* service name */
352
353         gss_OID         lgd_mech;       /* mech OID */
354         u_int           lgd_req_flags;  /* request flags */
355         gss_cred_id_t   lgd_cred;       /* credential */
356         gss_ctx_id_t    lgd_ctx;        /* session context */
357         gss_buffer_desc lgd_rmt_ctx;    /* remote handle of context */
358         uint32_t        lgd_seq_win;    /* sequence window */
359
360         int             lgd_rpc_err;
361         int             lgd_gss_err;
362 };
363
364 static int
365 do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
366             struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
367 {
368         char    *buf = NULL, *p = NULL, *end = NULL;
369         unsigned int timeout = 0; /* XXX decide on a reasonable value */
370         unsigned int buf_size = 0;
371
372         printerr(2, "doing downcall\n");
373         buf_size = sizeof(updata->seq) + sizeof(timeout) +
374                 sizeof(lgd->lgd_seq_win) +
375                 sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
376                 sizeof(context_token->length) + context_token->length;
377         p = buf = malloc(buf_size);
378         end = buf + buf_size;
379
380         if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
381         /* Not setting any timeout for now: */
382         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
383         if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
384         if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
385         if (write_buffer(&p, end, context_token)) goto out_err;
386
387         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
388         if (buf) free(buf);
389         return 0;
390 out_err:
391         if (buf) free(buf);
392         printerr(0, "ERROR: Failed to write downcall!\n");
393         return -1;
394 }
395
396 static int
397 do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
398                   int rpc_err, int gss_err)
399 {
400         char    buf[1024];
401         char    *p = buf, *end = buf + 1024;
402         unsigned int timeout = 0;
403         int     zero = 0;
404
405         printerr(1, "doing error downcall\n");
406
407         if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
408         if (WRITE_BYTES(&p, end, timeout)) goto out_err;
409         /* use seq_win = 0 to indicate an error: */
410         if (WRITE_BYTES(&p, end, zero)) goto out_err;
411         if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
412         if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
413
414         if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
415         return 0;
416 out_err:
417         printerr(0, "Failed to write error downcall!\n");
418         return -1;
419 }
420
421 #if 0
422 /*
423  * Create an RPC connection and establish an authenticated
424  * gss context with a server.
425  */
426 int create_auth_rpc_client(struct clnt_info *clp,
427                            CLIENT **clnt_return,
428                            AUTH **auth_return,
429                            uid_t uid,
430                            int authtype)
431 {
432         CLIENT                  *rpc_clnt = NULL;
433         struct rpc_gss_sec      sec;
434         AUTH                    *auth = NULL;
435         uid_t                   save_uid = -1;
436         int                     retval = -1;
437         int                     errcode;
438         OM_uint32               min_stat;
439         char                    rpc_errmsg[1024];
440         int                     sockp = RPC_ANYSOCK;
441         int                     sendsz = 32768, recvsz = 32768;
442         struct addrinfo         ai_hints, *a = NULL;
443         char                    service[64];
444         char                    *at_sign;
445
446         /* Create the context as the user (not as root) */
447         save_uid = geteuid();
448         if (setfsuid(uid) != 0) {
449                 printerr(0, "WARNING: Failed to setfsuid for "
450                             "user with uid %d\n", uid);
451                 goto out_fail;
452         }
453         printerr(2, "creating context using fsuid %d (save_uid %d)\n",
454                         uid, save_uid);
455
456         sec.qop = GSS_C_QOP_DEFAULT;
457         sec.svc = RPCSEC_GSS_SVC_NONE;
458         sec.cred = GSS_C_NO_CREDENTIAL;
459         sec.req_flags = 0;
460         if (authtype == AUTHTYPE_KRB5) {
461                 sec.mech = (gss_OID)&krb5oid;
462                 sec.req_flags = GSS_C_MUTUAL_FLAG;
463         }
464         else if (authtype == AUTHTYPE_SPKM3) {
465                 sec.mech = (gss_OID)&spkm3oid;
466                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
467                  * Need a way to switch....
468                  */
469                 sec.req_flags = GSS_C_MUTUAL_FLAG;
470         }
471         else {
472                 printerr(0, "ERROR: Invalid authentication type (%d) "
473                         "in create_auth_rpc_client\n", authtype);
474                 goto out_fail;
475         }
476
477
478         if (authtype == AUTHTYPE_KRB5) {
479 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
480                 /*
481                  * Do this before creating rpc connection since we won't need
482                  * rpc connection if it fails!
483                  */
484                 if (limit_krb5_enctypes(&sec, uid)) {
485                         printerr(1, "WARNING: Failed while limiting krb5 "
486                                     "encryption types for user with uid %d\n",
487                                  uid);
488                         goto out_fail;
489                 }
490 #endif
491         }
492
493         /* create an rpc connection to the nfs server */
494
495         printerr(2, "creating %s client for server %s\n", clp->protocol,
496                         clp->servername);
497
498         memset(&ai_hints, '\0', sizeof(ai_hints));
499         ai_hints.ai_family = PF_INET;
500         ai_hints.ai_flags |= AI_CANONNAME;
501         if ((strcmp(clp->protocol, "tcp")) == 0) {
502                 ai_hints.ai_socktype = SOCK_STREAM;
503                 ai_hints.ai_protocol = IPPROTO_TCP;
504         } else if ((strcmp(clp->protocol, "udp")) == 0) {
505                 ai_hints.ai_socktype = SOCK_DGRAM;
506                 ai_hints.ai_protocol = IPPROTO_UDP;
507         } else {
508                 printerr(0, "WARNING: unrecognized protocol, '%s', requested "
509                          "for connection to server %s for user with uid %d",
510                          clp->protocol, clp->servername, uid);
511                 goto out_fail;
512         }
513
514         /* extract the service name from clp->servicename */
515         if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
516                 printerr(0, "WARNING: servicename (%s) not formatted as "
517                         "expected with service@host", clp->servicename);
518                 goto out_fail;
519         }
520         if ((at_sign - clp->servicename) >= sizeof(service)) {
521                 printerr(0, "WARNING: service portion of servicename (%s) "
522                         "is too long!", clp->servicename);
523                 goto out_fail;
524         }
525         strncpy(service, clp->servicename, at_sign - clp->servicename);
526         service[at_sign - clp->servicename] = '\0';
527
528         errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
529         if (errcode) {
530                 printerr(0, "WARNING: Error from getaddrinfo for server "
531                          "'%s': %s", clp->servername, gai_strerror(errcode));
532                 goto out_fail;
533         }
534
535         if (a == NULL) {
536                 printerr(0, "WARNING: No address information found for "
537                          "connection to server %s for user with uid %d",
538                          clp->servername, uid);
539                 goto out_fail;
540         }
541         if (a->ai_protocol == IPPROTO_TCP) {
542                 if ((rpc_clnt = clnttcp_create(
543                                         (struct sockaddr_in *) a->ai_addr,
544                                         clp->prog, clp->vers, &sockp,
545                                         sendsz, recvsz)) == NULL) {
546                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
547                                  "WARNING: can't create tcp rpc_clnt "
548                                  "for server %s for user with uid %d",
549                                  clp->servername, uid);
550                         printerr(0, "%s\n",
551                                  clnt_spcreateerror(rpc_errmsg));
552                         goto out_fail;
553                 }
554         } else if (a->ai_protocol == IPPROTO_UDP) {
555                 const struct timeval timeout = {5, 0};
556                 if ((rpc_clnt = clntudp_bufcreate(
557                                         (struct sockaddr_in *) a->ai_addr,
558                                         clp->prog, clp->vers, timeout,
559                                         &sockp, sendsz, recvsz)) == NULL) {
560                         snprintf(rpc_errmsg, sizeof(rpc_errmsg),
561                                  "WARNING: can't create udp rpc_clnt "
562                                  "for server %s for user with uid %d",
563                                  clp->servername, uid);
564                         printerr(0, "%s\n",
565                                  clnt_spcreateerror(rpc_errmsg));
566                         goto out_fail;
567                 }
568         } else {
569                 /* Shouldn't happen! */
570                 printerr(0, "ERROR: requested protocol '%s', but "
571                          "got addrinfo with protocol %d",
572                          clp->protocol, a->ai_protocol);
573                 goto out_fail;
574         }
575         /* We're done with this */
576         freeaddrinfo(a);
577         a = NULL;
578
579         printerr(2, "creating context with server %s\n", clp->servicename);
580         auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
581         if (!auth) {
582                 /* Our caller should print appropriate message */
583                 printerr(2, "WARNING: Failed to create %s context for "
584                             "user with uid %d for server %s\n",
585                         (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
586                          uid, clp->servername);
587                 goto out_fail;
588         }
589
590         /* Success !!! */
591         rpc_clnt->cl_auth = auth;
592         *clnt_return = rpc_clnt;
593         *auth_return = auth;
594         retval = 0;
595
596   out:
597         if (sec.cred != GSS_C_NO_CREDENTIAL)
598                 gss_release_cred(&min_stat, &sec.cred);
599         if (a != NULL) freeaddrinfo(a);
600         /* Restore euid to original value */
601         if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
602                 printerr(0, "WARNING: Failed to restore fsuid"
603                             " to uid %d from %d\n", save_uid, uid);
604         }
605         return retval;
606
607   out_fail:
608         /* Only destroy here if failure.  Otherwise, caller is responsible */
609         if (rpc_clnt) clnt_destroy(rpc_clnt);
610
611         goto out;
612 }
613 #endif
614
615 static
616 int do_negotiation(struct lustre_gss_data *lgd,
617                    gss_buffer_desc *gss_token,
618                    struct lustre_gss_init_res *gr,
619                    int timeout)
620 {
621         char *file = "/proc/fs/lustre/gss/init_channel";
622         struct lgssd_ioctl_param param;
623         struct passwd *pw;
624         int fd, ret;
625         char outbuf[8192];
626         unsigned int *p;
627         int res;
628
629         pw = getpwuid(lgd->lgd_uid);
630         if (!pw) {
631                 printerr(0, "no uid %u in local user database\n",
632                          lgd->lgd_uid);
633                 return -1;
634         }
635
636         param.version = GSSD_INTERFACE_VERSION;
637         param.uuid = lgd->lgd_uuid;
638         param.lustre_svc = lgd->lgd_lustre_svc;
639         param.uid = lgd->lgd_uid;
640         param.gid = pw->pw_gid;
641         param.send_token_size = gss_token->length;
642         param.send_token = (char *) gss_token->value;
643         param.reply_buf_size = sizeof(outbuf);
644         param.reply_buf = outbuf;
645
646         fd = open(file, O_RDWR);
647         if (fd < 0) {
648                 printerr(0, "can't open file %s\n", file);
649                 return -1;
650         }
651
652         ret = write(fd, &param, sizeof(param));
653
654         if (ret != sizeof(param)) {
655                 printerr(0, "lustre ioctl err: %d\n", strerror(errno));
656                 close(fd);
657                 return -1;
658         }
659         if (param.status) {
660                 close(fd);
661                 printerr(0, "status: %d (%s)\n",
662                          param.status, strerror((int)param.status));
663                 if (param.status == -ETIMEDOUT) {
664                         /* kernel return -ETIMEDOUT means the rpc timedout,
665                          * we should notify the caller to reinitiate the
666                          * gss negotiation, by return -ERESTART
667                          */
668                         lgd->lgd_rpc_err = -ERESTART;
669                         lgd->lgd_gss_err = 0;
670                 } else {
671                         lgd->lgd_rpc_err = param.status;
672                         lgd->lgd_gss_err = 0;
673                 }
674                 return -1;
675         }
676         p = (unsigned int *)outbuf;
677         res = *p++;
678         gr->gr_major = *p++;
679         gr->gr_minor = *p++;
680         gr->gr_win = *p++;
681
682         gr->gr_ctx.length = *p++;
683         gr->gr_ctx.value = malloc(gr->gr_ctx.length);
684         memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
685         p += (((gr->gr_ctx.length + 3) & ~3) / 4);
686
687         gr->gr_token.length = *p++;
688         gr->gr_token.value = malloc(gr->gr_token.length);
689         memcpy(gr->gr_token.value, p, gr->gr_token.length);
690         p += (((gr->gr_token.length + 3) & ~3) / 4);
691
692         printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
693                  gr->gr_ctx.length, gr->gr_token.length);
694         close(fd);
695         return 0;
696 }
697
698 static
699 int gssd_refresh_lgd(struct lustre_gss_data *lgd)
700 {
701         struct lustre_gss_init_res gr;
702         gss_buffer_desc         *recv_tokenp, send_token;
703         OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
704
705         /* GSS context establishment loop. */
706         memset(&gr, 0, sizeof(gr));
707         recv_tokenp = GSS_C_NO_BUFFER;
708
709         for (;;) {
710                 /* print the token we just received */
711                 if (recv_tokenp != GSS_C_NO_BUFFER) {
712                         printerr(3, "The received token length %d\n",
713                                  recv_tokenp->length);
714                         print_hexl(3, recv_tokenp->value, recv_tokenp->length);
715                 }
716
717                 maj_stat = gss_init_sec_context(&min_stat,
718                                                 lgd->lgd_cred,
719                                                 &lgd->lgd_ctx,
720                                                 lgd->lgd_name,
721                                                 lgd->lgd_mech,
722                                                 lgd->lgd_req_flags,
723                                                 0,              /* time req */
724                                                 NULL,           /* channel */
725                                                 recv_tokenp,
726                                                 NULL,           /* used mech */
727                                                 &send_token,
728                                                 &ret_flags,
729                                                 NULL);          /* time rec */
730
731                 if (recv_tokenp != GSS_C_NO_BUFFER) {
732                         gss_release_buffer(&min_stat, &gr.gr_token);
733                         recv_tokenp = GSS_C_NO_BUFFER;
734                 }
735                 if (maj_stat != GSS_S_COMPLETE &&
736                     maj_stat != GSS_S_CONTINUE_NEEDED) {
737                         pgsserr("gss_init_sec_context", maj_stat, min_stat,
738                                 lgd->lgd_mech);
739                         break;
740                 }
741                 if (send_token.length != 0) {
742                         memset(&gr, 0, sizeof(gr));
743
744                         /* print the token we are about to send */
745                         printerr(3, "token being sent length %d\n",
746                                  send_token.length);
747                         print_hexl(3, send_token.value, send_token.length);
748
749                         call_stat = do_negotiation(lgd, &send_token, &gr, 0);
750                         gss_release_buffer(&min_stat, &send_token);
751
752                         if (call_stat != 0 ||
753                             (gr.gr_major != GSS_S_COMPLETE &&
754                              gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
755                                 printerr(0, "call stat %d, major stat 0x%x\n",
756                                          (int)call_stat, gr.gr_major);
757                                 return -1;
758                         }
759
760                         if (gr.gr_ctx.length != 0) {
761                                 if (lgd->lgd_rmt_ctx.value)
762                                         gss_release_buffer(&min_stat,
763                                                            &lgd->lgd_rmt_ctx);
764                                 lgd->lgd_rmt_ctx = gr.gr_ctx;
765                         }
766                         if (gr.gr_token.length != 0) {
767                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
768                                         break;
769                                 recv_tokenp = &gr.gr_token;
770                         }
771                 }
772
773                 /* GSS_S_COMPLETE => check gss header verifier,
774                  * usually checked in gss_validate
775                  */
776                 if (maj_stat == GSS_S_COMPLETE) {
777                         lgd->lgd_established = 1;
778                         lgd->lgd_seq_win = gr.gr_win;
779                         break;
780                 }
781         }
782         /* End context negotiation loop. */
783         if (!lgd->lgd_established) {
784                 if (gr.gr_token.length != 0)
785                         gss_release_buffer(&min_stat, &gr.gr_token);
786
787                 printerr(0, "context negotiation failed\n");
788                 return -1;
789         }
790
791         printerr(2, "successfully refreshed lgd\n");
792         return 0;
793 }
794
795 static
796 int gssd_create_lgd(struct clnt_info *clp,
797                     struct lustre_gss_data *lgd,
798                     struct lgssd_upcall_data *updata,
799                     int authtype)
800 {
801         gss_buffer_desc         sname;
802         OM_uint32               maj_stat, min_stat;
803         int                     retval = -1;
804
805         lgd->lgd_established = 0;
806         lgd->lgd_lustre_svc = updata->svc;
807         lgd->lgd_uid = updata->uid;
808         lgd->lgd_uuid = updata->obd;
809
810         switch (authtype) {
811         case AUTHTYPE_KRB5:
812                 lgd->lgd_mech = (gss_OID) &krb5oid;
813                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
814                 break;
815         case AUTHTYPE_SPKM3:
816                 lgd->lgd_mech = (gss_OID) &spkm3oid;
817                 /* XXX sec.req_flags = GSS_C_ANON_FLAG;
818                  * Need a way to switch....
819                  */
820                 lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
821                 break;
822         default:
823                 printerr(0, "Invalid authentication type (%d)\n", authtype);
824                 return -1;
825         }
826
827         lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
828         lgd->lgd_ctx = GSS_C_NO_CONTEXT;
829         lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
830         lgd->lgd_seq_win = 0;
831
832         sname.value = clp->servicename;
833         sname.length = strlen(clp->servicename);
834
835         maj_stat = gss_import_name(&min_stat, &sname,
836                                    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
837                                    &lgd->lgd_name);
838         if (maj_stat != GSS_S_COMPLETE) {
839                 pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
840                 goto out_fail;
841         }
842
843         retval = gssd_refresh_lgd(lgd);
844
845         if (lgd->lgd_name != GSS_C_NO_NAME)
846                 gss_release_name(&min_stat, &lgd->lgd_name);
847
848         if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
849                 gss_release_cred(&min_stat, &lgd->lgd_cred);
850
851   out_fail:
852         return retval;
853 }
854
855 static
856 void gssd_free_lgd(struct lustre_gss_data *lgd)
857 {
858         gss_buffer_t            token = GSS_C_NO_BUFFER;
859         OM_uint32               maj_stat, min_stat;
860
861         if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
862                 return;
863
864         maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
865 }
866
867 static
868 int construct_service_name(struct clnt_info *clp,
869                            struct lgssd_upcall_data *ud)
870 {
871         const int buflen = 256;
872         char name[buflen];
873
874         if (clp->servicename) {
875                 free(clp->servicename);
876                 clp->servicename = NULL;
877         }
878
879         if (ptl_nid2hostname(ud->nid, name, buflen))
880                 return -1;
881
882         clp->servicename = malloc(32 + strlen(name));
883         if (!clp->servicename) {
884                 printerr(0, "can't alloc memory\n");
885                 return -1;
886         }
887         sprintf(clp->servicename, "%s@%s",
888                 ud->svc == LUSTRE_GSS_SVC_MDS ?
889                 GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
890                 name);
891         printerr(2, "constructed servicename: %s\n", clp->servicename);
892         return 0;
893 }
894
895 /*
896  * this code uses the userland rpcsec gss library to create a krb5
897  * context on behalf of the kernel
898  */
899 void
900 handle_krb5_upcall(struct clnt_info *clp)
901 {
902         gss_buffer_desc         token;
903         struct lgssd_upcall_data updata;
904         struct lustre_gss_data  lgd;
905         char                    **credlist = NULL;
906         char                    **ccname;
907
908         printerr(2, "handling krb5 upcall\n");
909
910         lgd.lgd_rpc_err = -EPERM; /* default error code */
911
912         token.length = 0;
913         token.value = NULL;
914
915         if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
916                 printerr(0, "WARNING: failed reading from krb5 "
917                             "upcall pipe: %s\n", strerror(errno));
918                 goto out;
919         }
920
921         printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
922                  updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
923
924         if (updata.svc != LUSTRE_GSS_SVC_MDS &&
925             updata.svc != LUSTRE_GSS_SVC_OSS) {
926                 printerr(0, "invalid svc %d\n", updata.svc);
927                 lgd.lgd_rpc_err = -EPROTO;
928                 goto out_return_error;
929         }
930         updata.obd[sizeof(updata.obd)-1] = '\0';
931
932         if (construct_service_name(clp, &updata)) {
933                 printerr(0, "failed to construct service name\n");
934                 goto out_return_error;
935         }
936
937         if (updata.uid == 0) {
938                 int success = 0;
939
940                 /*
941                  * Get a list of credential cache names and try each
942                  * of them until one works or we've tried them all
943                  */
944                 if (gssd_get_krb5_machine_cred_list(&credlist)) {
945                         printerr(0, "ERROR: Failed to obtain machine "
946                                     "credentials for %s\n", clp->servicename);
947                         goto out_return_error;
948                 }
949                 for (ccname = credlist; ccname && *ccname; ccname++) {
950                         gssd_setup_krb5_machine_gss_ccache(*ccname);
951                         if ((gssd_create_lgd(clp, &lgd, &updata,
952                                              AUTHTYPE_KRB5)) == 0) {
953                                 /* Success! */
954                                 success++;
955                                 break;
956                         }
957                         printerr(2, "WARNING: Failed to create krb5 context "
958                                     "for user with uid %d with credentials "
959                                     "cache %s for service %s\n",
960                                  updata.uid, *ccname, clp->servicename);
961                 }
962                 gssd_free_krb5_machine_cred_list(credlist);
963                 if (!success) {
964                         printerr(0, "ERROR: Failed to create krb5 context "
965                                     "for user with uid %d with any "
966                                     "credentials cache for service %s\n",
967                                  updata.uid, clp->servicename);
968                         goto out_return_error;
969                 }
970         }
971         else {
972                 /* Tell krb5 gss which credentials cache to use */
973                 gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
974
975                 if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
976                         printerr(0, "WARNING: Failed to create krb5 context "
977                                     "for user with uid %d for service %s\n",
978                                  updata.uid, clp->servicename);
979                         goto out_return_error;
980                 }
981         }
982
983         if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
984                 printerr(0, "WARNING: Failed to serialize krb5 context for "
985                             "user with uid %d for service %s\n",
986                          updata.uid, clp->servicename);
987                 goto out_return_error;
988         }
989
990         printerr(1, "refreshed: %u@%s for %s\n",
991                  updata.uid, updata.obd, clp->servicename);
992         do_downcall(clp->krb5_fd, &updata, &lgd, &token);
993
994 out:
995         if (token.value)
996                 free(token.value);
997
998         gssd_free_lgd(&lgd);
999         return;
1000
1001 out_return_error:
1002         do_error_downcall(clp->krb5_fd, &updata,
1003                           lgd.lgd_rpc_err, lgd.lgd_gss_err);
1004         goto out;
1005 }
1006
1007 /*
1008  * this code uses the userland rpcsec gss library to create an spkm3
1009  * context on behalf of the kernel
1010  */
1011 void
1012 handle_spkm3_upcall(struct clnt_info *clp)
1013 {
1014 #if 0
1015         uid_t                   uid;
1016         CLIENT                  *rpc_clnt = NULL;
1017         AUTH                    *auth = NULL;
1018         struct authgss_private_data pd;
1019         gss_buffer_desc         token;
1020
1021         printerr(2, "handling spkm3 upcall\n");
1022
1023         token.length = 0;
1024         token.value = NULL;
1025
1026         if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
1027                 printerr(0, "WARNING: failed reading uid from spkm3 "
1028                          "upcall pipe: %s\n", strerror(errno));
1029                 goto out;
1030         }
1031
1032         if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
1033                 printerr(0, "WARNING: Failed to create spkm3 context for "
1034                             "user with uid %d\n", uid);
1035                 goto out_return_error;
1036         }
1037
1038         if (!authgss_get_private_data(auth, &pd)) {
1039                 printerr(0, "WARNING: Failed to obtain authentication "
1040                             "data for user with uid %d for server %s\n",
1041                          uid, clp->servername);
1042                 goto out_return_error;
1043         }
1044
1045         if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
1046                 printerr(0, "WARNING: Failed to serialize spkm3 context for "
1047                             "user with uid %d for server\n",
1048                          uid, clp->servername);
1049                 goto out_return_error;
1050         }
1051
1052         do_downcall(clp->spkm3_fd, uid, &pd, &token);
1053
1054 out:
1055         if (token.value)
1056                 free(token.value);
1057         if (auth)
1058                 AUTH_DESTROY(auth);
1059         if (rpc_clnt)
1060                 clnt_destroy(rpc_clnt);
1061         return;
1062
1063 out_return_error:
1064         do_error_downcall(clp->spkm3_fd, uid, -1);
1065         goto out;
1066 #endif
1067 }