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