nfs-ganesha 1.4
|
00001 /* 00002 gssd_proc.c 00003 00004 Copyright (c) 2000-2004 The Regents of the University of Michigan. 00005 All rights reserved. 00006 00007 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 00008 Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>. 00009 Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>. 00010 Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU> 00011 Copyright (c) 2004 Kevin Coffman <kwc@umich.edu> 00012 All rights reserved, all wrongs reversed. 00013 00014 Redistribution and use in source and binary forms, with or without 00015 modification, are permitted provided that the following conditions 00016 are met: 00017 00018 1. Redistributions of source code must retain the above copyright 00019 notice, this list of conditions and the following disclaimer. 00020 2. Redistributions in binary form must reproduce the above copyright 00021 notice, this list of conditions and the following disclaimer in the 00022 documentation and/or other materials provided with the distribution. 00023 3. Neither the name of the University nor the names of its 00024 contributors may be used to endorse or promote products derived 00025 from this software without specific prior written permission. 00026 00027 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 00028 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00029 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00030 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00031 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00032 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00033 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 00034 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00035 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00036 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00037 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00038 00039 */ 00040 00041 #ifdef HAVE_CONFIG_H 00042 #include <config.h> 00043 #endif /* HAVE_CONFIG_H */ 00044 00045 #ifndef _GNU_SOURCE 00046 #define _GNU_SOURCE 00047 #endif 00048 00049 #include <sys/param.h> 00050 #include <rpc/rpc.h> 00051 #include <sys/stat.h> 00052 #include <sys/socket.h> 00053 #include <arpa/inet.h> 00054 #include <sys/fsuid.h> 00055 00056 #include <stdio.h> 00057 #include <stdlib.h> 00058 #include <pwd.h> 00059 #include <grp.h> 00060 #include <string.h> 00061 #include <dirent.h> 00062 #include <poll.h> 00063 #include <fcntl.h> 00064 #include <signal.h> 00065 #include <unistd.h> 00066 #include <errno.h> 00067 #include <gssapi/gssapi.h> 00068 #include <netdb.h> 00069 00070 #include "gssd.h" 00071 #include "err_util.h" 00072 #include "gss_util.h" 00073 #include "krb5_util.h" 00074 #include "context.h" 00075 #include "nfsrpc.h" 00076 #include "nfslib.h" 00077 00078 /* 00079 * pollarray: 00080 * array of struct pollfd suitable to pass to poll. initialized to 00081 * zero - a zero struct is ignored by poll() because the events mask is 0. 00082 * 00083 * clnt_list: 00084 * linked list of struct clnt_info which associates a clntXXX directory 00085 * with an index into pollarray[], and other basic data about that client. 00086 * 00087 * Directory structure: created by the kernel 00088 * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel 00089 * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants 00090 * a context, write the resulting context 00091 * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name 00092 * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using 00093 * a text-based string of parameters 00094 * 00095 * Algorithm: 00096 * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files. When data is ready, 00097 * read and process; performs rpcsec_gss context initialization protocol to 00098 * get a cred for that user. Writes result to corresponding krb5 file 00099 * in a form the kernel code will understand. 00100 * In addition, we make sure we are notified whenever anything is 00101 * created or destroyed in {rpc_pipefs} or in any of the clntXX directories, 00102 * and rescan the whole {rpc_pipefs} when this happens. 00103 */ 00104 00105 struct pollfd * pollarray; 00106 00107 int pollsize; /* the size of pollaray (in pollfd's) */ 00108 00109 /* 00110 * convert a presentation address string to a sockaddr_storage struct. Returns 00111 * true on success or false on failure. 00112 * 00113 * Note that we do not populate the sin6_scope_id field here for IPv6 addrs. 00114 * gssd nececessarily relies on hostname resolution and DNS AAAA records 00115 * do not generally contain scope-id's. This means that GSSAPI auth really 00116 * can't work with IPv6 link-local addresses. 00117 * 00118 * We *could* consider changing this if we did something like adopt the 00119 * Microsoft "standard" of using the ipv6-literal.net domainname, but it's 00120 * not really feasible at present. 00121 */ 00122 static int 00123 addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) 00124 { 00125 int rc; 00126 struct addrinfo *res; 00127 struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; 00128 00129 #ifndef IPV6_SUPPORTED 00130 hints.ai_family = AF_INET; 00131 #endif /* IPV6_SUPPORTED */ 00132 00133 rc = getaddrinfo(node, port, &hints, &res); 00134 if (rc) { 00135 printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n", 00136 node, port, rc == EAI_SYSTEM ? strerror(errno) : 00137 gai_strerror(rc)); 00138 return 0; 00139 } 00140 00141 #ifdef IPV6_SUPPORTED 00142 /* 00143 * getnameinfo ignores the scopeid. If the address turns out to have 00144 * a non-zero scopeid, we can't use it -- the resolved host might be 00145 * completely different from the one intended. 00146 */ 00147 if (res->ai_addr->sa_family == AF_INET6) { 00148 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; 00149 if (sin6->sin6_scope_id) { 00150 printerr(0, "ERROR: address %s has non-zero " 00151 "sin6_scope_id!\n", node); 00152 freeaddrinfo(res); 00153 return 0; 00154 } 00155 } 00156 #endif /* IPV6_SUPPORTED */ 00157 00158 memcpy(sa, res->ai_addr, res->ai_addrlen); 00159 freeaddrinfo(res); 00160 return 1; 00161 } 00162 00163 /* 00164 * convert a sockaddr to a hostname 00165 */ 00166 static char * 00167 sockaddr_to_hostname(const struct sockaddr *sa, const char *addr) 00168 { 00169 socklen_t addrlen; 00170 int err; 00171 char *hostname; 00172 char hbuf[NI_MAXHOST]; 00173 00174 switch (sa->sa_family) { 00175 case AF_INET: 00176 addrlen = sizeof(struct sockaddr_in); 00177 break; 00178 #ifdef IPV6_SUPPORTED 00179 case AF_INET6: 00180 addrlen = sizeof(struct sockaddr_in6); 00181 break; 00182 #endif /* IPV6_SUPPORTED */ 00183 default: 00184 printerr(0, "ERROR: unrecognized addr family %d\n", 00185 sa->sa_family); 00186 return NULL; 00187 } 00188 00189 err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, 00190 NI_NAMEREQD); 00191 if (err) { 00192 printerr(0, "ERROR: unable to resolve %s to hostname: %s\n", 00193 addr, err == EAI_SYSTEM ? strerror(err) : 00194 gai_strerror(err)); 00195 return NULL; 00196 } 00197 00198 hostname = strdup(hbuf); 00199 00200 return hostname; 00201 } 00202 00203 /* XXX buffer problems: */ 00204 static int 00205 read_service_info(char *info_file_name, char **servicename, char **servername, 00206 int *prog, int *vers, char **protocol, 00207 struct sockaddr *addr) { 00208 #define INFOBUFLEN 256 00209 char buf[INFOBUFLEN + 1]; 00210 static char dummy[128]; 00211 int nbytes; 00212 static char service[128]; 00213 static char address[128]; 00214 char program[16]; 00215 char version[16]; 00216 char protoname[16]; 00217 char port[128]; 00218 char *p; 00219 int fd = -1; 00220 int numfields; 00221 00222 *servicename = *servername = *protocol = NULL; 00223 00224 if ((fd = open(info_file_name, O_RDONLY)) == -1) { 00225 printerr(0, "ERROR: can't open %s: %s\n", info_file_name, 00226 strerror(errno)); 00227 goto fail; 00228 } 00229 if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1) 00230 goto fail; 00231 close(fd); 00232 buf[nbytes] = '\0'; 00233 00234 numfields = sscanf(buf,"RPC server: %127s\n" 00235 "service: %127s %15s version %15s\n" 00236 "address: %127s\n" 00237 "protocol: %15s\n", 00238 dummy, 00239 service, program, version, 00240 address, 00241 protoname); 00242 00243 if (numfields == 5) { 00244 strcpy(protoname, "tcp"); 00245 } else if (numfields != 6) { 00246 goto fail; 00247 } 00248 00249 port[0] = '\0'; 00250 if ((p = strstr(buf, "port")) != NULL) 00251 sscanf(p, "port: %127s\n", port); 00252 00253 /* check service, program, and version */ 00254 if (memcmp(service, "nfs", 3) != 0) 00255 return -1; 00256 *prog = atoi(program + 1); /* skip open paren */ 00257 *vers = atoi(version); 00258 00259 if (strlen(service) == 3 ) { 00260 if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) && 00261 (*vers != 4))) 00262 goto fail; 00263 } else if (memcmp(service, "nfs4_cb", 7) == 0) { 00264 if (*vers != 1) 00265 goto fail; 00266 } 00267 00268 if (!addrstr_to_sockaddr(addr, address, port)) 00269 goto fail; 00270 00271 *servername = sockaddr_to_hostname(addr, address); 00272 if (*servername == NULL) 00273 goto fail; 00274 00275 nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername); 00276 if (nbytes > INFOBUFLEN) 00277 goto fail; 00278 00279 if (!(*servicename = calloc(strlen(buf) + 1, 1))) 00280 goto fail; 00281 memcpy(*servicename, buf, strlen(buf)); 00282 00283 if (!(*protocol = strdup(protoname))) 00284 goto fail; 00285 return 0; 00286 fail: 00287 printerr(0, "ERROR: failed to read service info\n"); 00288 if (fd != -1) close(fd); 00289 free(*servername); 00290 free(*servicename); 00291 free(*protocol); 00292 *servicename = *servername = *protocol = NULL; 00293 return -1; 00294 } 00295 00296 static void 00297 destroy_client(struct clnt_info *clp) 00298 { 00299 if (clp->krb5_poll_index != -1) 00300 memset(&pollarray[clp->krb5_poll_index], 0, 00301 sizeof(struct pollfd)); 00302 if (clp->spkm3_poll_index != -1) 00303 memset(&pollarray[clp->spkm3_poll_index], 0, 00304 sizeof(struct pollfd)); 00305 if (clp->gssd_poll_index != -1) 00306 memset(&pollarray[clp->gssd_poll_index], 0, 00307 sizeof(struct pollfd)); 00308 if (clp->dir_fd != -1) close(clp->dir_fd); 00309 if (clp->krb5_fd != -1) close(clp->krb5_fd); 00310 if (clp->spkm3_fd != -1) close(clp->spkm3_fd); 00311 if (clp->gssd_fd != -1) close(clp->gssd_fd); 00312 free(clp->dirname); 00313 free(clp->servicename); 00314 free(clp->servername); 00315 free(clp->protocol); 00316 free(clp); 00317 } 00318 00319 static struct clnt_info * 00320 insert_new_clnt(void) 00321 { 00322 struct clnt_info *clp = NULL; 00323 00324 if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) { 00325 printerr(0, "ERROR: can't malloc clnt_info: %s\n", 00326 strerror(errno)); 00327 goto out; 00328 } 00329 clp->krb5_poll_index = -1; 00330 clp->spkm3_poll_index = -1; 00331 clp->gssd_poll_index = -1; 00332 clp->krb5_fd = -1; 00333 clp->spkm3_fd = -1; 00334 clp->gssd_fd = -1; 00335 clp->dir_fd = -1; 00336 00337 TAILQ_INSERT_HEAD(&clnt_list, clp, list); 00338 out: 00339 return clp; 00340 } 00341 00342 static int 00343 process_clnt_dir_files(struct clnt_info * clp) 00344 { 00345 char name[PATH_MAX]; 00346 char gname[PATH_MAX]; 00347 char info_file_name[PATH_MAX]; 00348 00349 if (clp->gssd_fd == -1) { 00350 snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); 00351 clp->gssd_fd = open(gname, O_RDWR); 00352 } 00353 if (clp->gssd_fd == -1) { 00354 if (clp->krb5_fd == -1) { 00355 snprintf(name, sizeof(name), "%s/krb5", clp->dirname); 00356 clp->krb5_fd = open(name, O_RDWR); 00357 } 00358 if (clp->spkm3_fd == -1) { 00359 snprintf(name, sizeof(name), "%s/spkm3", clp->dirname); 00360 clp->spkm3_fd = open(name, O_RDWR); 00361 } 00362 00363 /* If we opened a gss-specific pipe, let's try opening 00364 * the new upcall pipe again. If we succeed, close 00365 * gss-specific pipe(s). 00366 */ 00367 if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) { 00368 clp->gssd_fd = open(gname, O_RDWR); 00369 if (clp->gssd_fd != -1) { 00370 if (clp->krb5_fd != -1) 00371 close(clp->krb5_fd); 00372 clp->krb5_fd = -1; 00373 if (clp->spkm3_fd != -1) 00374 close(clp->spkm3_fd); 00375 clp->spkm3_fd = -1; 00376 } 00377 } 00378 } 00379 00380 if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) && 00381 (clp->gssd_fd == -1)) 00382 return -1; 00383 snprintf(info_file_name, sizeof(info_file_name), "%s/info", 00384 clp->dirname); 00385 if ((clp->servicename == NULL) && 00386 read_service_info(info_file_name, &clp->servicename, 00387 &clp->servername, &clp->prog, &clp->vers, 00388 &clp->protocol, (struct sockaddr *) &clp->addr)) 00389 return -1; 00390 return 0; 00391 } 00392 00393 static int 00394 get_poll_index(int *ind) 00395 { 00396 int i; 00397 00398 *ind = -1; 00399 for (i=0; i<FD_ALLOC_BLOCK; i++) { 00400 if (pollarray[i].events == 0) { 00401 *ind = i; 00402 break; 00403 } 00404 } 00405 if (*ind == -1) { 00406 printerr(0, "ERROR: No pollarray slots open\n"); 00407 return -1; 00408 } 00409 return 0; 00410 } 00411 00412 00413 static int 00414 insert_clnt_poll(struct clnt_info *clp) 00415 { 00416 if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) { 00417 if (get_poll_index(&clp->gssd_poll_index)) { 00418 printerr(0, "ERROR: Too many gssd clients\n"); 00419 return -1; 00420 } 00421 pollarray[clp->gssd_poll_index].fd = clp->gssd_fd; 00422 pollarray[clp->gssd_poll_index].events |= POLLIN; 00423 } 00424 00425 if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { 00426 if (get_poll_index(&clp->krb5_poll_index)) { 00427 printerr(0, "ERROR: Too many krb5 clients\n"); 00428 return -1; 00429 } 00430 pollarray[clp->krb5_poll_index].fd = clp->krb5_fd; 00431 pollarray[clp->krb5_poll_index].events |= POLLIN; 00432 } 00433 00434 if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) { 00435 if (get_poll_index(&clp->spkm3_poll_index)) { 00436 printerr(0, "ERROR: Too many spkm3 clients\n"); 00437 return -1; 00438 } 00439 pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd; 00440 pollarray[clp->spkm3_poll_index].events |= POLLIN; 00441 } 00442 00443 return 0; 00444 } 00445 00446 static void 00447 process_clnt_dir(char *dir, char *pdir) 00448 { 00449 struct clnt_info * clp; 00450 00451 if (!(clp = insert_new_clnt())) 00452 goto fail_destroy_client; 00453 00454 /* An extra for the '/', and an extra for the null */ 00455 if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { 00456 goto fail_destroy_client; 00457 } 00458 sprintf(clp->dirname, "%s/%s", pdir, dir); 00459 if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) { 00460 printerr(0, "ERROR: can't open %s: %s\n", 00461 clp->dirname, strerror(errno)); 00462 goto fail_destroy_client; 00463 } 00464 fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL); 00465 fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT); 00466 00467 if (process_clnt_dir_files(clp)) 00468 goto fail_keep_client; 00469 00470 if (insert_clnt_poll(clp)) 00471 goto fail_destroy_client; 00472 00473 return; 00474 00475 fail_destroy_client: 00476 if (clp) { 00477 TAILQ_REMOVE(&clnt_list, clp, list); 00478 destroy_client(clp); 00479 } 00480 fail_keep_client: 00481 /* We couldn't find some subdirectories, but we keep the client 00482 * around in case we get a notification on the directory when the 00483 * subdirectories are created. */ 00484 return; 00485 } 00486 00487 void 00488 init_client_list(void) 00489 { 00490 TAILQ_INIT(&clnt_list); 00491 /* Eventually plan to grow/shrink poll array: */ 00492 pollsize = FD_ALLOC_BLOCK; 00493 pollarray = calloc(pollsize, sizeof(struct pollfd)); 00494 } 00495 00496 /* 00497 * This is run after a DNOTIFY signal, and should clear up any 00498 * directories that are no longer around, and re-scan any existing 00499 * directories, since the DNOTIFY could have been in there. 00500 */ 00501 static void 00502 update_old_clients(struct dirent **namelist, int size, char *pdir) 00503 { 00504 struct clnt_info *clp; 00505 void *saveprev; 00506 int i, stillhere; 00507 char fname[PATH_MAX]; 00508 00509 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { 00510 /* only compare entries in the global list that are from the 00511 * same pipefs parent directory as "pdir" 00512 */ 00513 if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue; 00514 00515 stillhere = 0; 00516 for (i=0; i < size; i++) { 00517 snprintf(fname, sizeof(fname), "%s/%s", 00518 pdir, namelist[i]->d_name); 00519 if (strcmp(clp->dirname, fname) == 0) { 00520 stillhere = 1; 00521 break; 00522 } 00523 } 00524 if (!stillhere) { 00525 printerr(2, "destroying client %s\n", clp->dirname); 00526 saveprev = clp->list.tqe_prev; 00527 TAILQ_REMOVE(&clnt_list, clp, list); 00528 destroy_client(clp); 00529 clp = saveprev; 00530 } 00531 } 00532 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { 00533 if (!process_clnt_dir_files(clp)) 00534 insert_clnt_poll(clp); 00535 } 00536 } 00537 00538 /* Search for a client by directory name, return 1 if found, 0 otherwise */ 00539 static int 00540 find_client(char *dirname, char *pdir) 00541 { 00542 struct clnt_info *clp; 00543 char fname[PATH_MAX]; 00544 00545 for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { 00546 snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname); 00547 if (strcmp(clp->dirname, fname) == 0) 00548 return 1; 00549 } 00550 return 0; 00551 } 00552 00553 static int 00554 process_pipedir(char *pipe_name) 00555 { 00556 struct dirent **namelist; 00557 int i, j; 00558 00559 if (chdir(pipe_name) < 0) { 00560 printerr(0, "ERROR: can't chdir to %s: %s\n", 00561 pipe_name, strerror(errno)); 00562 return -1; 00563 } 00564 00565 j = scandir(pipe_name, &namelist, NULL, alphasort); 00566 if (j < 0) { 00567 printerr(0, "ERROR: can't scandir %s: %s\n", 00568 pipe_name, strerror(errno)); 00569 return -1; 00570 } 00571 00572 update_old_clients(namelist, j, pipe_name); 00573 for (i=0; i < j; i++) { 00574 if (i < FD_ALLOC_BLOCK 00575 && !strncmp(namelist[i]->d_name, "clnt", 4) 00576 && !find_client(namelist[i]->d_name, pipe_name)) 00577 process_clnt_dir(namelist[i]->d_name, pipe_name); 00578 free(namelist[i]); 00579 } 00580 00581 free(namelist); 00582 00583 return 0; 00584 } 00585 00586 /* Used to read (and re-read) list of clients, set up poll array. */ 00587 int 00588 update_client_list(void) 00589 { 00590 int retval = -1; 00591 struct topdirs_info *tdi; 00592 00593 TAILQ_FOREACH(tdi, &topdirs_list, list) { 00594 retval = process_pipedir(tdi->dirname); 00595 if (retval) 00596 printerr(1, "WARNING: error processing %s\n", 00597 tdi->dirname); 00598 00599 } 00600 return retval; 00601 } 00602 00603 /* 00604 * Parse the supported encryption type information 00605 */ 00606 static int 00607 parse_enctypes(char *enctypes) 00608 { 00609 int n = 0; 00610 char *curr, *comma; 00611 int i; 00612 static char *cached_types; 00613 00614 if (cached_types && strcmp(cached_types, enctypes) == 0) 00615 return 0; 00616 free(cached_types); 00617 00618 if (krb5_enctypes != NULL) { 00619 free(krb5_enctypes); 00620 krb5_enctypes = NULL; 00621 num_krb5_enctypes = 0; 00622 } 00623 00624 /* count the number of commas */ 00625 for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { 00626 comma = strchr(curr, ','); 00627 if (comma != NULL) 00628 n++; 00629 else 00630 break; 00631 } 00632 /* If no more commas and we're not at the end, there's one more value */ 00633 if (*curr != '\0') 00634 n++; 00635 00636 /* Empty string, return an error */ 00637 if (n == 0) 00638 return ENOENT; 00639 00640 /* Allocate space for enctypes array */ 00641 if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { 00642 return ENOMEM; 00643 } 00644 00645 /* Now parse each value into the array */ 00646 for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { 00647 krb5_enctypes[i++] = atoi(curr); 00648 comma = strchr(curr, ','); 00649 if (comma == NULL) 00650 break; 00651 } 00652 00653 num_krb5_enctypes = n; 00654 if ((cached_types = malloc(strlen(enctypes)+1))) 00655 strcpy(cached_types, enctypes); 00656 00657 return 0; 00658 } 00659 00660 static int 00661 do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, 00662 gss_buffer_desc *context_token) 00663 { 00664 char *buf = NULL, *p = NULL, *end = NULL; 00665 unsigned int timeout = context_timeout; 00666 unsigned int buf_size = 0; 00667 00668 printerr(1, "doing downcall\n"); 00669 buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) + 00670 sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length + 00671 sizeof(context_token->length) + context_token->length; 00672 p = buf = malloc(buf_size); 00673 end = buf + buf_size; 00674 00675 if (WRITE_BYTES(&p, end, uid)) goto out_err; 00676 if (WRITE_BYTES(&p, end, timeout)) goto out_err; 00677 if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err; 00678 if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err; 00679 if (write_buffer(&p, end, context_token)) goto out_err; 00680 00681 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err; 00682 if (buf) free(buf); 00683 return 0; 00684 out_err: 00685 if (buf) free(buf); 00686 printerr(1, "Failed to write downcall!\n"); 00687 return -1; 00688 } 00689 00690 static int 00691 do_error_downcall(int k5_fd, uid_t uid, int err) 00692 { 00693 char buf[1024]; 00694 char *p = buf, *end = buf + 1024; 00695 unsigned int timeout = 0; 00696 int zero = 0; 00697 00698 printerr(1, "doing error downcall\n"); 00699 00700 if (WRITE_BYTES(&p, end, uid)) goto out_err; 00701 if (WRITE_BYTES(&p, end, timeout)) goto out_err; 00702 /* use seq_win = 0 to indicate an error: */ 00703 if (WRITE_BYTES(&p, end, zero)) goto out_err; 00704 if (WRITE_BYTES(&p, end, err)) goto out_err; 00705 00706 if (write(k5_fd, buf, p - buf) < p - buf) goto out_err; 00707 return 0; 00708 out_err: 00709 printerr(1, "Failed to write error downcall!\n"); 00710 return -1; 00711 } 00712 00713 /* 00714 * If the port isn't already set, do an rpcbind query to the remote server 00715 * using the program and version and get the port. 00716 * 00717 * Newer kernels send the value of the port= mount option in the "info" 00718 * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value 00719 * of the port= option or '2049'. The port field in a new sockaddr should 00720 * reflect the value that was sent by the kernel. 00721 */ 00722 static int 00723 populate_port(struct sockaddr *sa, const socklen_t salen, 00724 const rpcprog_t program, const rpcvers_t version, 00725 const unsigned short protocol) 00726 { 00727 struct sockaddr_in *s4 = (struct sockaddr_in *) sa; 00728 #ifdef IPV6_SUPPORTED 00729 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; 00730 #endif /* IPV6_SUPPORTED */ 00731 unsigned short port; 00732 00733 /* 00734 * Newer kernels send the port in the upcall. If we already have 00735 * the port, there's no need to look it up. 00736 */ 00737 switch (sa->sa_family) { 00738 case AF_INET: 00739 if (s4->sin_port != 0) { 00740 printerr(2, "DEBUG: port already set to %d\n", 00741 ntohs(s4->sin_port)); 00742 return 1; 00743 } 00744 break; 00745 #ifdef IPV6_SUPPORTED 00746 case AF_INET6: 00747 if (s6->sin6_port != 0) { 00748 printerr(2, "DEBUG: port already set to %d\n", 00749 ntohs(s6->sin6_port)); 00750 return 1; 00751 } 00752 break; 00753 #endif /* IPV6_SUPPORTED */ 00754 default: 00755 printerr(0, "ERROR: unsupported address family %d\n", 00756 sa->sa_family); 00757 return 0; 00758 } 00759 00760 /* 00761 * Newer kernels that send the port in the upcall set the value to 00762 * 2049 for NFSv4 mounts when one isn't specified. The check below is 00763 * only for kernels that don't send the port in the upcall. For those 00764 * we either have to do an rpcbind query or set it to the standard 00765 * port. Doing a query could be problematic (firewalls, etc), so take 00766 * the latter approach. 00767 */ 00768 if (program == 100003 && version == 4) { 00769 port = 2049; 00770 goto set_port; 00771 } 00772 00773 port = nfs_getport(sa, salen, program, version, protocol); 00774 if (!port) { 00775 printerr(0, "ERROR: unable to obtain port for prog %ld " 00776 "vers %ld\n", program, version); 00777 return 0; 00778 } 00779 00780 set_port: 00781 printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port, 00782 program, version); 00783 00784 switch (sa->sa_family) { 00785 case AF_INET: 00786 s4->sin_port = htons(port); 00787 break; 00788 #ifdef IPV6_SUPPORTED 00789 case AF_INET6: 00790 s6->sin6_port = htons(port); 00791 break; 00792 #endif /* IPV6_SUPPORTED */ 00793 } 00794 00795 return 1; 00796 } 00797 00798 /* 00799 * Create an RPC connection and establish an authenticated 00800 * gss context with a server. 00801 */ 00802 int create_auth_rpc_client(struct clnt_info *clp, 00803 CLIENT **clnt_return, 00804 AUTH **auth_return, 00805 uid_t uid, 00806 int authtype) 00807 { 00808 CLIENT *rpc_clnt = NULL; 00809 struct rpc_gss_sec sec; 00810 AUTH *auth = NULL; 00811 uid_t save_uid = -1; 00812 int retval = -1; 00813 OM_uint32 min_stat; 00814 char rpc_errmsg[1024]; 00815 int protocol; 00816 struct timeval timeout = {5, 0}; 00817 struct sockaddr *addr = (struct sockaddr *) &clp->addr; 00818 socklen_t salen; 00819 00820 /* Create the context as the user (not as root) */ 00821 save_uid = geteuid(); 00822 if (setfsuid(uid) != 0) { 00823 printerr(0, "WARNING: Failed to setfsuid for " 00824 "user with uid %d\n", uid); 00825 goto out_fail; 00826 } 00827 printerr(2, "creating context using fsuid %d (save_uid %d)\n", 00828 uid, save_uid); 00829 00830 sec.qop = GSS_C_QOP_DEFAULT; 00831 sec.svc = RPCSEC_GSS_SVC_NONE; 00832 sec.cred = GSS_C_NO_CREDENTIAL; 00833 sec.req_flags = 0; 00834 if (authtype == AUTHTYPE_KRB5) { 00835 sec.mech = (gss_OID)&krb5oid; 00836 sec.req_flags = GSS_C_MUTUAL_FLAG; 00837 } 00838 else if (authtype == AUTHTYPE_SPKM3) { 00839 sec.mech = (gss_OID)&spkm3oid; 00840 /* XXX sec.req_flags = GSS_C_ANON_FLAG; 00841 * Need a way to switch.... 00842 */ 00843 sec.req_flags = GSS_C_MUTUAL_FLAG; 00844 } 00845 else { 00846 printerr(0, "ERROR: Invalid authentication type (%d) " 00847 "in create_auth_rpc_client\n", authtype); 00848 goto out_fail; 00849 } 00850 00851 00852 if (authtype == AUTHTYPE_KRB5) { 00853 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES 00854 /* 00855 * Do this before creating rpc connection since we won't need 00856 * rpc connection if it fails! 00857 */ 00858 if (limit_krb5_enctypes(&sec)) { 00859 printerr(1, "WARNING: Failed while limiting krb5 " 00860 "encryption types for user with uid %d\n", 00861 uid); 00862 goto out_fail; 00863 } 00864 #endif 00865 } 00866 00867 /* create an rpc connection to the nfs server */ 00868 00869 printerr(2, "creating %s client for server %s\n", clp->protocol, 00870 clp->servername); 00871 00872 if ((strcmp(clp->protocol, "tcp")) == 0) { 00873 protocol = IPPROTO_TCP; 00874 } else if ((strcmp(clp->protocol, "udp")) == 0) { 00875 protocol = IPPROTO_UDP; 00876 } else { 00877 printerr(0, "WARNING: unrecognized protocol, '%s', requested " 00878 "for connection to server %s for user with uid %d\n", 00879 clp->protocol, clp->servername, uid); 00880 goto out_fail; 00881 } 00882 00883 switch (addr->sa_family) { 00884 case AF_INET: 00885 salen = sizeof(struct sockaddr_in); 00886 break; 00887 #ifdef IPV6_SUPPORTED 00888 case AF_INET6: 00889 salen = sizeof(struct sockaddr_in6); 00890 break; 00891 #endif /* IPV6_SUPPORTED */ 00892 default: 00893 printerr(1, "ERROR: Unknown address family %d\n", 00894 addr->sa_family); 00895 goto out_fail; 00896 } 00897 00898 if (!populate_port(addr, salen, clp->prog, clp->vers, protocol)) 00899 goto out_fail; 00900 00901 rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog, 00902 clp->vers, &timeout); 00903 if (!rpc_clnt) { 00904 snprintf(rpc_errmsg, sizeof(rpc_errmsg), 00905 "WARNING: can't create %s rpc_clnt to server %s for " 00906 "user with uid %d", 00907 protocol == IPPROTO_TCP ? "tcp" : "udp", 00908 clp->servername, uid); 00909 printerr(0, "%s\n", 00910 clnt_spcreateerror(rpc_errmsg)); 00911 goto out_fail; 00912 } 00913 00914 printerr(2, "creating context with server %s\n", clp->servicename); 00915 auth = authgss_create_default(rpc_clnt, clp->servicename, &sec); 00916 if (!auth) { 00917 /* Our caller should print appropriate message */ 00918 printerr(2, "WARNING: Failed to create %s context for " 00919 "user with uid %d for server %s\n", 00920 (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"), 00921 uid, clp->servername); 00922 goto out_fail; 00923 } 00924 00925 /* Success !!! */ 00926 rpc_clnt->cl_auth = auth; 00927 *clnt_return = rpc_clnt; 00928 *auth_return = auth; 00929 retval = 0; 00930 00931 out: 00932 if (sec.cred != GSS_C_NO_CREDENTIAL) 00933 gss_release_cred(&min_stat, &sec.cred); 00934 /* Restore euid to original value */ 00935 if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) { 00936 printerr(0, "WARNING: Failed to restore fsuid" 00937 " to uid %d from %d\n", save_uid, uid); 00938 } 00939 return retval; 00940 00941 out_fail: 00942 /* Only destroy here if failure. Otherwise, caller is responsible */ 00943 if (rpc_clnt) clnt_destroy(rpc_clnt); 00944 00945 goto out; 00946 } 00947 00948 /* 00949 * this code uses the userland rpcsec gss library to create a krb5 00950 * context on behalf of the kernel 00951 */ 00952 static void 00953 process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, 00954 char *service) 00955 { 00956 CLIENT *rpc_clnt = NULL; 00957 AUTH *auth = NULL; 00958 struct authgss_private_data pd; 00959 gss_buffer_desc token; 00960 char **credlist = NULL; 00961 char **ccname; 00962 char **dirname; 00963 int create_resp = -1; 00964 int err, downcall_err = -EACCES; 00965 00966 printerr(1, "handling krb5 upcall (%s)\n", clp->dirname); 00967 00968 if (tgtname) { 00969 if (clp->servicename) { 00970 free(clp->servicename); 00971 clp->servicename = strdup(tgtname); 00972 } 00973 } 00974 token.length = 0; 00975 token.value = NULL; 00976 memset(&pd, 0, sizeof(struct authgss_private_data)); 00977 00978 /* 00979 * If "service" is specified, then the kernel is indicating that 00980 * we must use machine credentials for this request. (Regardless 00981 * of the uid value or the setting of root_uses_machine_creds.) 00982 * If the service value is "*", then any service name can be used. 00983 * Otherwise, it specifies the service name that should be used. 00984 * (For now, the values of service will only be "*" or "nfs".) 00985 * 00986 * Restricting gssd to use "nfs" service name is needed for when 00987 * the NFS server is doing a callback to the NFS client. In this 00988 * case, the NFS server has to authenticate itself as "nfs" -- 00989 * even if there are other service keys such as "host" or "root" 00990 * in the keytab. 00991 * 00992 * Another case when the kernel may specify the service attribute 00993 * is when gssd is being asked to create the context for a 00994 * SETCLIENT_ID operation. In this case, machine credentials 00995 * must be used for the authentication. However, the service name 00996 * used for this case is not important. 00997 * 00998 */ 00999 printerr(2, "%s: service is '%s'\n", __func__, 01000 service ? service : "<null>"); 01001 if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && 01002 service == NULL)) { 01003 /* Tell krb5 gss which credentials cache to use */ 01004 for (dirname = ccachesearch; *dirname != NULL; dirname++) { 01005 err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname); 01006 if (err == -EKEYEXPIRED) 01007 downcall_err = -EKEYEXPIRED; 01008 else if (!err) 01009 create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, 01010 AUTHTYPE_KRB5); 01011 if (create_resp == 0) 01012 break; 01013 } 01014 } 01015 if (create_resp != 0) { 01016 if (uid == 0 && (root_uses_machine_creds == 1 || 01017 service != NULL)) { 01018 int nocache = 0; 01019 int success = 0; 01020 do { 01021 gssd_refresh_krb5_machine_credential(clp->servername, 01022 NULL, service); 01023 /* 01024 * Get a list of credential cache names and try each 01025 * of them until one works or we've tried them all 01026 */ 01027 if (gssd_get_krb5_machine_cred_list(&credlist)) { 01028 printerr(0, "ERROR: No credentials found " 01029 "for connection to server %s\n", 01030 clp->servername); 01031 goto out_return_error; 01032 } 01033 for (ccname = credlist; ccname && *ccname; ccname++) { 01034 gssd_setup_krb5_machine_gss_ccache(*ccname); 01035 if ((create_auth_rpc_client(clp, &rpc_clnt, 01036 &auth, uid, 01037 AUTHTYPE_KRB5)) == 0) { 01038 /* Success! */ 01039 success++; 01040 break; 01041 } 01042 printerr(2, "WARNING: Failed to create machine krb5 context " 01043 "with credentials cache %s for server %s\n", 01044 *ccname, clp->servername); 01045 } 01046 gssd_free_krb5_machine_cred_list(credlist); 01047 if (!success) { 01048 if(nocache == 0) { 01049 nocache++; 01050 printerr(2, "WARNING: Machine cache is prematurely expired or corrupted " 01051 "trying to recreate cache for server %s\n", clp->servername); 01052 } else { 01053 printerr(1, "WARNING: Failed to create machine krb5 context " 01054 "with any credentials cache for server %s\n", 01055 clp->servername); 01056 goto out_return_error; 01057 } 01058 } 01059 } while(!success); 01060 } else { 01061 printerr(1, "WARNING: Failed to create krb5 context " 01062 "for user with uid %d for server %s\n", 01063 uid, clp->servername); 01064 goto out_return_error; 01065 } 01066 } 01067 01068 if (!authgss_get_private_data(auth, &pd)) { 01069 printerr(1, "WARNING: Failed to obtain authentication " 01070 "data for user with uid %d for server %s\n", 01071 uid, clp->servername); 01072 goto out_return_error; 01073 } 01074 01075 if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid, NULL)) { 01076 printerr(0, "WARNING: Failed to serialize krb5 context for " 01077 "user with uid %d for server %s\n", 01078 uid, clp->servername); 01079 goto out_return_error; 01080 } 01081 01082 do_downcall(fd, uid, &pd, &token); 01083 01084 out: 01085 if (token.value) 01086 free(token.value); 01087 #ifndef HAVE_LIBTIRPC 01088 if (pd.pd_ctx_hndl.length != 0) 01089 authgss_free_private_data(&pd); 01090 #endif 01091 if (auth) 01092 AUTH_DESTROY(auth); 01093 if (rpc_clnt) 01094 clnt_destroy(rpc_clnt); 01095 return; 01096 01097 out_return_error: 01098 do_error_downcall(fd, uid, downcall_err); 01099 goto out; 01100 } 01101 01102 /* 01103 * this code uses the userland rpcsec gss library to create an spkm3 01104 * context on behalf of the kernel 01105 */ 01106 static void 01107 process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd) 01108 { 01109 CLIENT *rpc_clnt = NULL; 01110 AUTH *auth = NULL; 01111 struct authgss_private_data pd; 01112 gss_buffer_desc token; 01113 01114 printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname); 01115 01116 token.length = 0; 01117 token.value = NULL; 01118 01119 if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { 01120 printerr(0, "WARNING: Failed to create spkm3 context for " 01121 "user with uid %d\n", uid); 01122 goto out_return_error; 01123 } 01124 01125 if (!authgss_get_private_data(auth, &pd)) { 01126 printerr(0, "WARNING: Failed to obtain authentication " 01127 "data for user with uid %d for server %s\n", 01128 uid, clp->servername); 01129 goto out_return_error; 01130 } 01131 01132 if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid, NULL)) { 01133 printerr(0, "WARNING: Failed to serialize spkm3 context for " 01134 "user with uid %d for server\n", 01135 uid, clp->servername); 01136 goto out_return_error; 01137 } 01138 01139 do_downcall(fd, uid, &pd, &token); 01140 01141 out: 01142 if (token.value) 01143 free(token.value); 01144 if (auth) 01145 AUTH_DESTROY(auth); 01146 if (rpc_clnt) 01147 clnt_destroy(rpc_clnt); 01148 return; 01149 01150 out_return_error: 01151 do_error_downcall(fd, uid, -1); 01152 goto out; 01153 } 01154 01155 void 01156 handle_krb5_upcall(struct clnt_info *clp) 01157 { 01158 uid_t uid; 01159 01160 if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { 01161 printerr(0, "WARNING: failed reading uid from krb5 " 01162 "upcall pipe: %s\n", strerror(errno)); 01163 return; 01164 } 01165 01166 return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL); 01167 } 01168 01169 void 01170 handle_spkm3_upcall(struct clnt_info *clp) 01171 { 01172 uid_t uid; 01173 01174 if (read(clp->spkm3_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { 01175 printerr(0, "WARNING: failed reading uid from spkm3 " 01176 "upcall pipe: %s\n", strerror(errno)); 01177 return; 01178 } 01179 01180 return process_spkm3_upcall(clp, uid, clp->spkm3_fd); 01181 } 01182 01183 void 01184 handle_gssd_upcall(struct clnt_info *clp) 01185 { 01186 uid_t uid; 01187 char *lbuf = NULL; 01188 int lbuflen = 0; 01189 char *p; 01190 char *mech = NULL; 01191 char *target = NULL; 01192 char *service = NULL; 01193 char *enctypes = NULL; 01194 01195 printerr(1, "handling gssd upcall (%s)\n", clp->dirname); 01196 01197 if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) { 01198 printerr(0, "WARNING: handle_gssd_upcall: " 01199 "failed reading request\n"); 01200 return; 01201 } 01202 printerr(2, "%s: '%s'\n", __func__, lbuf); 01203 01204 /* find the mechanism name */ 01205 if ((p = strstr(lbuf, "mech=")) != NULL) { 01206 mech = malloc(lbuflen); 01207 if (!mech) 01208 goto out; 01209 if (sscanf(p, "mech=%s", mech) != 1) { 01210 printerr(0, "WARNING: handle_gssd_upcall: " 01211 "failed to parse gss mechanism name " 01212 "in upcall string '%s'\n", lbuf); 01213 goto out; 01214 } 01215 } else { 01216 printerr(0, "WARNING: handle_gssd_upcall: " 01217 "failed to find gss mechanism name " 01218 "in upcall string '%s'\n", lbuf); 01219 goto out; 01220 } 01221 01222 /* read uid */ 01223 if ((p = strstr(lbuf, "uid=")) != NULL) { 01224 if (sscanf(p, "uid=%d", &uid) != 1) { 01225 printerr(0, "WARNING: handle_gssd_upcall: " 01226 "failed to parse uid " 01227 "in upcall string '%s'\n", lbuf); 01228 goto out; 01229 } 01230 } else { 01231 printerr(0, "WARNING: handle_gssd_upcall: " 01232 "failed to find uid " 01233 "in upcall string '%s'\n", lbuf); 01234 goto out; 01235 } 01236 01237 /* read supported encryption types if supplied */ 01238 if ((p = strstr(lbuf, "enctypes=")) != NULL) { 01239 enctypes = malloc(lbuflen); 01240 if (!enctypes) 01241 goto out; 01242 if (sscanf(p, "enctypes=%s", enctypes) != 1) { 01243 printerr(0, "WARNING: handle_gssd_upcall: " 01244 "failed to parse encryption types " 01245 "in upcall string '%s'\n", lbuf); 01246 goto out; 01247 } 01248 if (parse_enctypes(enctypes) != 0) { 01249 printerr(0, "WARNING: handle_gssd_upcall: " 01250 "parsing encryption types failed: errno %d\n", errno); 01251 } 01252 } 01253 01254 /* read target name */ 01255 if ((p = strstr(lbuf, "target=")) != NULL) { 01256 target = malloc(lbuflen); 01257 if (!target) 01258 goto out; 01259 if (sscanf(p, "target=%s", target) != 1) { 01260 printerr(0, "WARNING: handle_gssd_upcall: " 01261 "failed to parse target name " 01262 "in upcall string '%s'\n", lbuf); 01263 goto out; 01264 } 01265 } 01266 01267 /* 01268 * read the service name 01269 * 01270 * The presence of attribute "service=" indicates that machine 01271 * credentials should be used for this request. If the value 01272 * is "*", then any machine credentials available can be used. 01273 * If the value is anything else, then machine credentials for 01274 * the specified service name (always "nfs" for now) should be 01275 * used. 01276 */ 01277 if ((p = strstr(lbuf, "service=")) != NULL) { 01278 service = malloc(lbuflen); 01279 if (!service) 01280 goto out; 01281 if (sscanf(p, "service=%s", service) != 1) { 01282 printerr(0, "WARNING: handle_gssd_upcall: " 01283 "failed to parse service type " 01284 "in upcall string '%s'\n", lbuf); 01285 goto out; 01286 } 01287 } 01288 01289 if (strcmp(mech, "krb5") == 0) 01290 process_krb5_upcall(clp, uid, clp->gssd_fd, target, service); 01291 else if (strcmp(mech, "spkm3") == 0) 01292 process_spkm3_upcall(clp, uid, clp->gssd_fd); 01293 else 01294 printerr(0, "WARNING: handle_gssd_upcall: " 01295 "received unknown gss mech '%s'\n", mech); 01296 01297 out: 01298 free(lbuf); 01299 free(mech); 01300 free(enctypes); 01301 free(target); 01302 free(service); 01303 return; 01304 } 01305