nfs-ganesha 1.4

gssd_proc.c

Go to the documentation of this file.
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