nfs-ganesha 1.4

krb5_util.c

Go to the documentation of this file.
00001 /*
00002  *  Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
00003  *  http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
00004  *
00005  *  Copyright (c) 2002-2004 The Regents of the University of Michigan.
00006  *  All rights reserved.
00007  *
00008  *  Andy Adamson <andros@umich.edu>
00009  *  J. Bruce Fields <bfields@umich.edu>
00010  *  Marius Aamodt Eriksen <marius@umich.edu>
00011  *  Kevin Coffman <kwc@umich.edu>
00012  */
00013 
00014 /*
00015  * slave/kprop.c
00016  *
00017  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
00018  * All Rights Reserved.
00019  *
00020  * Export of this software from the United States of America may
00021  *   require a specific license from the United States Government.
00022  *   It is the responsibility of any person or organization contemplating
00023  *   export to obtain such a license before exporting.
00024  *
00025  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
00026  * distribute this software and its documentation for any purpose and
00027  * without fee is hereby granted, provided that the above copyright
00028  * notice appear in all copies and that both that copyright notice and
00029  * this permission notice appear in supporting documentation, and that
00030  * the name of M.I.T. not be used in advertising or publicity pertaining
00031  * to distribution of the software without specific, written prior
00032  * permission.  Furthermore if you modify this software you must label
00033  * your software as modified software and not distribute it in such a
00034  * fashion that it might be confused with the original M.I.T. software.
00035  * M.I.T. makes no representations about the suitability of
00036  * this software for any purpose.  It is provided "as is" without express
00037  * or implied warranty.
00038  */
00039 
00040 /*
00041  * Copyright 1994 by OpenVision Technologies, Inc.
00042  *
00043  * Permission to use, copy, modify, distribute, and sell this software
00044  * and its documentation for any purpose is hereby granted without fee,
00045  * provided that the above copyright notice appears in all copies and
00046  * that both that copyright notice and this permission notice appear in
00047  * supporting documentation, and that the name of OpenVision not be used
00048  * in advertising or publicity pertaining to distribution of the software
00049  * without specific, written prior permission. OpenVision makes no
00050  * representations about the suitability of this software for any
00051  * purpose.  It is provided "as is" without express or implied warranty.
00052  *
00053  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
00054  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
00055  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
00056  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
00057  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
00058  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00059  * PERFORMANCE OF THIS SOFTWARE.
00060  */
00061 /*
00062   krb5_util.c
00063 
00064   Copyright (c) 2004 The Regents of the University of Michigan.
00065   All rights reserved.
00066 
00067   Redistribution and use in source and binary forms, with or without
00068   modification, are permitted provided that the following conditions
00069   are met:
00070 
00071   1. Redistributions of source code must retain the above copyright
00072      notice, this list of conditions and the following disclaimer.
00073   2. Redistributions in binary form must reproduce the above copyright
00074      notice, this list of conditions and the following disclaimer in the
00075      documentation and/or other materials provided with the distribution.
00076   3. Neither the name of the University nor the names of its
00077      contributors may be used to endorse or promote products derived
00078      from this software without specific prior written permission.
00079 
00080   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00081   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00082   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00083   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00084   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00085   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00086   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
00087   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00088   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00089   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00090   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00091 
00092 */
00093 
00094 #ifdef HAVE_CONFIG_H
00095 #include <config.h>
00096 #endif  /* HAVE_CONFIG_H */
00097 
00098 #ifndef _GNU_SOURCE
00099 #define _GNU_SOURCE
00100 #endif
00101 
00102 #include <sys/param.h>
00103 #include <rpc/rpc.h>
00104 #include <sys/stat.h>
00105 #include <sys/socket.h>
00106 #include <arpa/inet.h>
00107 
00108 #include <stdio.h>
00109 #include <stdlib.h>
00110 #include <unistd.h>
00111 #include <string.h>
00112 #include <pthread.h>
00113 #include <dirent.h>
00114 #include <netdb.h>
00115 #include <ctype.h>
00116 #include <errno.h>
00117 #include <time.h>
00118 #include <gssapi/gssapi.h>
00119 #ifdef USE_PRIVATE_KRB5_FUNCTIONS
00120 #include <gssapi/gssapi_krb5.h>
00121 #endif
00122 #include <krb5.h>
00123 #include <rpc/auth_gss.h>
00124 
00125 #include "gssd.h"
00126 #include "err_util.h"
00127 #include "gss_util.h"
00128 #include "krb5_util.h"
00129 
00130 pthread_mutex_t ple_mtx = PTHREAD_MUTEX_INITIALIZER;
00131 
00132 /* Global list of principals/cache file names for machine credentials */
00133 struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
00134 
00135 /*==========================*/
00136 /*===  Internal routines ===*/
00137 /*==========================*/
00138 
00139 static int select_krb5_ccache(const struct dirent *d);
00140 static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
00141                 struct dirent **d);
00142 static int gssd_get_single_krb5_cred(krb5_context context,
00143                 krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache);
00144 static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
00145                 char **ret_realm);
00146 
00147 /*
00148  * Called from the scandir function to weed out potential krb5
00149  * credentials cache files
00150  *
00151  * Returns:
00152  *      0 => don't select this one
00153  *      1 => select this one
00154  */
00155 static int
00156 select_krb5_ccache(const struct dirent *d)
00157 {
00158         /*
00159          * Note: We used to check d->d_type for DT_REG here,
00160          * but apparenlty reiser4 always has DT_UNKNOWN.
00161          * Check for IS_REG after stat() call instead.
00162          */
00163         if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
00164                 return 1;
00165         else
00166                 return 0;
00167 }
00168 
00169 /*
00170  * Look in directory "dirname" for files that look like they
00171  * are Kerberos Credential Cache files for a given UID.  Return
00172  * non-zero and the dirent pointer for the entry most likely to be
00173  * what we want. Otherwise, return zero and no dirent pointer.
00174  * The caller is responsible for freeing the dirent if one is returned.
00175  *
00176  * Returns 0 if a valid-looking entry was found and a non-zero error
00177  * code otherwise.
00178  */
00179 static int
00180 gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
00181 {
00182         struct dirent **namelist;
00183         int n;
00184         int i;
00185         int found = 0;
00186         struct dirent *best_match_dir = NULL;
00187         struct stat best_match_stat, tmp_stat;
00188         char buf[1030];
00189         char *princname = NULL;
00190         char *realm = NULL;
00191         int score, best_match_score = 0, err = -EACCES;
00192 
00193         memset(&best_match_stat, 0, sizeof(best_match_stat));
00194         *d = NULL;
00195         n = scandir(dirname, &namelist, select_krb5_ccache, 0);
00196         if (n < 0) {
00197                 printerr(1, "Error doing scandir on directory '%s': %s\n",
00198                         dirname, strerror(errno));
00199         }
00200         else if (n > 0) {
00201                 char statname[1024];
00202                 for (i = 0; i < n; i++) {
00203                         snprintf(statname, sizeof(statname),
00204                                  "%s/%s", dirname, namelist[i]->d_name);
00205                         printerr(3, "CC file '%s' being considered, "
00206                                  "with preferred realm '%s'\n",
00207                                  statname, preferred_realm ?
00208                                         preferred_realm : "<none selected>");
00209                         snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, 
00210                                         namelist[i]->d_name);
00211                         if (lstat(statname, &tmp_stat)) {
00212                                 printerr(0, "Error doing stat on file '%s'\n",
00213                                          statname);
00214                                 free(namelist[i]);
00215                                 continue;
00216                         }
00217                         /* Only pick caches owned by the user (uid) */
00218                         if (tmp_stat.st_uid != uid) {
00219                                 printerr(3, "CC file '%s' owned by %u, not %u\n",
00220                                          statname, tmp_stat.st_uid, uid);
00221                                 free(namelist[i]);
00222                                 continue;
00223                         }
00224                         if (!S_ISREG(tmp_stat.st_mode)) {
00225                                 printerr(3, "CC file '%s' is not a regular file\n",
00226                                          statname);
00227                                 free(namelist[i]);
00228                                 continue;
00229                         }
00230                         if (uid == 0 && !root_uses_machine_creds && 
00231                                 strstr(namelist[i]->d_name, "_machine_")) {
00232                                 printerr(3, "CC file '%s' not available to root\n",
00233                                          statname);
00234                                 free(namelist[i]);
00235                                 continue;
00236                         }
00237                         if (!query_krb5_ccache(buf, &princname, &realm)) {
00238                                 printerr(3, "CC file '%s' is expired or corrupt\n",
00239                                          statname);
00240                                 free(namelist[i]);
00241                                 err = -EKEYEXPIRED;
00242                                 continue;
00243                         }
00244 
00245                         score = 0;
00246                         if (preferred_realm &&
00247                                         strcmp(realm, preferred_realm) == 0) 
00248                                 score++;
00249 
00250                         printerr(3, "CC file '%s'(%s@%s) passed all checks and"
00251                                     " has mtime of %u\n",
00252                                  statname, princname, realm, 
00253                                  tmp_stat.st_mtime);
00254                         /*
00255                          * if more than one match is found, return the most
00256                          * recent (the one with the latest mtime), and
00257                          * don't free the dirent
00258                          */
00259                         if (!found) {
00260                                 best_match_dir = namelist[i];
00261                                 best_match_stat = tmp_stat;
00262                                 best_match_score = score;
00263                                 found++;
00264                         }
00265                         else {
00266                                 /*
00267                                  * If current score is higher than best match 
00268                                  * score, we use the current match. Otherwise,
00269                                  * if the current match has an mtime later
00270                                  * than the one we are looking at, then use
00271                                  * the current match.  Otherwise, we still
00272                                  * have the best match.
00273                                  */
00274                                 if (best_match_score < score ||
00275                                     (best_match_score == score && 
00276                                        tmp_stat.st_mtime >
00277                                             best_match_stat.st_mtime)) {
00278                                         free(best_match_dir);
00279                                         best_match_dir = namelist[i];
00280                                         best_match_stat = tmp_stat;
00281                                         best_match_score = score;
00282                                 }
00283                                 else {
00284                                         free(namelist[i]);
00285                                 }
00286                                 printerr(3, "CC file '%s/%s' is our "
00287                                             "current best match "
00288                                             "with mtime of %u\n",
00289                                          dirname, best_match_dir->d_name,
00290                                          best_match_stat.st_mtime);
00291                         }
00292                         free(princname);
00293                         free(realm);
00294                 }
00295                 free(namelist);
00296         }
00297         if (found) {
00298                 *d = best_match_dir;
00299                 return 0;
00300         }
00301 
00302         return err;
00303 }
00304 
00305 /*
00306  * Obtain credentials via a key in the keytab given
00307  * a keytab handle and a gssd_k5_kt_princ structure.
00308  * Checks to see if current credentials are expired,
00309  * if not, uses the keytab to obtain new credentials.
00310  *
00311  * Returns:
00312  *      0 => success (or credentials have not expired)
00313  *      nonzero => error
00314  */
00315 static int
00316 gssd_get_single_krb5_cred(krb5_context context,
00317                           krb5_keytab kt,
00318                           struct gssd_k5_kt_princ *ple,
00319                           int nocache)
00320 {
00321 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
00322         krb5_get_init_creds_opt *init_opts = NULL;
00323 #else
00324         krb5_get_init_creds_opt options;
00325 #endif
00326         krb5_get_init_creds_opt *opts;
00327         krb5_creds my_creds;
00328         krb5_ccache ccache = NULL;
00329         char kt_name[BUFSIZ];
00330         char cc_name[BUFSIZ];
00331         int code;
00332         time_t now = time(0);
00333         char *cache_type;
00334         char *pname = NULL;
00335         char *k5err = NULL;
00336 
00337         memset(&my_creds, 0, sizeof(my_creds));
00338 
00339         if (ple->ccname && ple->endtime > now && !nocache) {
00340                 printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
00341                          ple->ccname, ple->endtime);
00342                 code = 0;
00343                 goto out;
00344         }
00345 
00346         if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
00347                 printerr(0, "ERROR: Unable to get keytab name in "
00348                             "gssd_get_single_krb5_cred\n");
00349                 goto out;
00350         }
00351 
00352         if ((krb5_unparse_name(context, ple->princ, &pname)))
00353                 pname = NULL;
00354 
00355 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
00356         code = krb5_get_init_creds_opt_alloc(context, &init_opts);
00357         if (code) {
00358                 k5err = gssd_k5_err_msg(context, code);
00359                 printerr(0, "ERROR: %s allocating gic options\n", k5err);
00360                 goto out;
00361         }
00362         if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
00363                 printerr(1, "WARNING: Unable to set option for addressless "
00364                          "tickets.  May have problems behind a NAT.\n");
00365 #ifdef TEST_SHORT_LIFETIME
00366         /* set a short lifetime (for debugging only!) */
00367         printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
00368         krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
00369 #endif
00370         opts = init_opts;
00371 
00372 #else   /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */
00373 
00374         krb5_get_init_creds_opt_init(&options);
00375         krb5_get_init_creds_opt_set_address_list(&options, NULL);
00376 #ifdef TEST_SHORT_LIFETIME
00377         /* set a short lifetime (for debugging only!) */
00378         printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
00379         krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
00380 #endif
00381         opts = &options;
00382 #endif
00383 
00384         if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
00385                                                kt, 0, NULL, opts))) {
00386                 k5err = gssd_k5_err_msg(context, code);
00387                 printerr(1, "WARNING: %s while getting initial ticket for "
00388                          "principal '%s' using keytab '%s'\n", k5err,
00389                          pname ? pname : "<unparsable>", kt_name);
00390                 goto out;
00391         }
00392 
00393         /*
00394          * Initialize cache file which we're going to be using
00395          */
00396 
00397         if (use_memcache)
00398             cache_type = "MEMORY";
00399         else
00400             cache_type = "FILE";
00401         snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
00402                 cache_type,
00403                 ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
00404                 GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
00405         ple->endtime = my_creds.times.endtime;
00406         if (ple->ccname != NULL)
00407                 free(ple->ccname);
00408         ple->ccname = strdup(cc_name);
00409         if (ple->ccname == NULL) {
00410                 printerr(0, "ERROR: no storage to duplicate credentials "
00411                             "cache name '%s'\n", cc_name);
00412                 code = ENOMEM;
00413                 goto out;
00414         }
00415         if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
00416                 k5err = gssd_k5_err_msg(context, code);
00417                 printerr(0, "ERROR: %s while opening credential cache '%s'\n",
00418                          k5err, cc_name);
00419                 goto out;
00420         }
00421         if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
00422                 k5err = gssd_k5_err_msg(context, code);
00423                 printerr(0, "ERROR: %s while initializing credential "
00424                          "cache '%s'\n", k5err, cc_name);
00425         }
00426         if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
00427                 k5err = gssd_k5_err_msg(context, code);
00428                 printerr(0, "ERROR: %s while storing credentials in '%s'\n",
00429                          k5err, cc_name);
00430                 goto out;
00431         }
00432         /* if we get this far, let gss mech know */
00433         gssd_set_krb5_ccache_name(cc_name);
00434         code = 0;
00435         printerr(2, "Successfully obtained machine credentials for "
00436                  "principal '%s' stored in ccache '%s'\n", pname, cc_name);
00437   out:
00438 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
00439         if (init_opts)
00440                 krb5_get_init_creds_opt_free(context, init_opts);
00441 #endif
00442         if (pname)
00443                 k5_free_unparsed_name(context, pname);
00444         if (ccache)
00445                 krb5_cc_close(context, ccache);
00446         krb5_free_cred_contents(context, &my_creds);
00447         free(k5err);
00448         return (code);
00449 }
00450 
00451 /*
00452  * Depending on the version of Kerberos, we either need to use
00453  * a private function, or simply set the environment variable.
00454  */
00455 void
00456 gssd_set_krb5_ccache_name(char *ccname)
00457 {
00458 #ifdef USE_GSS_KRB5_CCACHE_NAME
00459         u_int   maj_stat, min_stat;
00460 
00461         printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
00462                  ccname);
00463         maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
00464         if (maj_stat != GSS_S_COMPLETE) {
00465                 printerr(0, "WARNING: gss_krb5_ccache_name with "
00466                         "name '%s' failed (%s)\n",
00467                         ccname, error_message(min_stat));
00468         }
00469 #else
00470         /*
00471          * Set the KRB5CCNAME environment variable to tell the krb5 code
00472          * which credentials cache to use.  (Instead of using the private
00473          * function above for which there is no generic gssapi
00474          * equivalent.)
00475          */
00476         printerr(2, "using environment variable to select krb5 ccache %s\n",
00477                  ccname);
00478         setenv("KRB5CCNAME", ccname, 1);
00479 #endif
00480 }
00481 
00482 /*
00483  * Given a principal, find a matching ple structure
00484  */
00485 static struct gssd_k5_kt_princ *
00486 find_ple_by_princ(krb5_context context, krb5_principal princ)
00487 {
00488         struct gssd_k5_kt_princ *ple;
00489 
00490         for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
00491                 if (krb5_principal_compare(context, ple->princ, princ))
00492                         return ple;
00493         }
00494         /* no match found */
00495         return NULL;
00496 }
00497 
00498 /*
00499  * Create, initialize, and add a new ple structure to the global list
00500  */
00501 static struct gssd_k5_kt_princ *
00502 new_ple(krb5_context context, krb5_principal princ)
00503 {
00504         struct gssd_k5_kt_princ *ple = NULL, *p;
00505         krb5_error_code code;
00506         char *default_realm;
00507         int is_default_realm = 0;
00508 
00509         ple = malloc(sizeof(struct gssd_k5_kt_princ));
00510         if (ple == NULL)
00511                 goto outerr;
00512         memset(ple, 0, sizeof(*ple));
00513 
00514 #ifdef HAVE_KRB5
00515         ple->realm = strndup(princ->realm.data,
00516                              princ->realm.length);
00517 #else
00518         ple->realm = strdup(princ->realm);
00519 #endif
00520         if (ple->realm == NULL)
00521                 goto outerr;
00522         code = krb5_copy_principal(context, princ, &ple->princ);
00523         if (code)
00524                 goto outerr;
00525 
00526         /*
00527          * Add new entry onto the list (if this is the default
00528          * realm, always add to the front of the list)
00529          */
00530 
00531         code = krb5_get_default_realm(context, &default_realm);
00532         if (code == 0) {
00533                 if (strcmp(ple->realm, default_realm) == 0)
00534                         is_default_realm = 1;
00535                 k5_free_default_realm(context, default_realm);
00536         }
00537 
00538         if (is_default_realm) {
00539                 ple->next = gssd_k5_kt_princ_list;
00540                 gssd_k5_kt_princ_list = ple;
00541         } else {
00542                 p = gssd_k5_kt_princ_list;
00543                 while (p != NULL && p->next != NULL)
00544                         p = p->next;
00545                 if (p == NULL)
00546                         gssd_k5_kt_princ_list = ple;
00547                 else
00548                         p->next = ple;
00549         }
00550 
00551         return ple;
00552 outerr:
00553         if (ple) {
00554                 if (ple->realm)
00555                         free(ple->realm);
00556                 free(ple);
00557         }
00558         return NULL;
00559 }
00560 
00561 /*
00562  * Given a principal, find an existing ple structure, or create one
00563  */
00564 static struct gssd_k5_kt_princ *
00565 get_ple_by_princ(krb5_context context, krb5_principal princ)
00566 {
00567         struct gssd_k5_kt_princ *ple;
00568 
00569         pthread_mutex_lock(&ple_mtx);
00570 
00571         ple = find_ple_by_princ(context, princ);
00572         if (ple == NULL) {
00573                 ple = new_ple(context, princ);
00574         }
00575 
00576         pthread_mutex_unlock(&ple_mtx);
00577 
00578         return ple;
00579 }
00580 
00581 /*
00582  * Given a (possibly unqualified) hostname,
00583  * return the fully qualified (lower-case!) hostname
00584  */
00585 static int
00586 get_full_hostname(const char *inhost, char *outhost, int outhostlen)
00587 {
00588         struct addrinfo *addrs = NULL;
00589         struct addrinfo hints;
00590         int retval;
00591         char *c;
00592 
00593         memset(&hints, 0, sizeof(hints));
00594         hints.ai_socktype = SOCK_STREAM;
00595         hints.ai_family = PF_UNSPEC;
00596         hints.ai_flags = AI_CANONNAME;
00597 
00598         /* Get full target hostname */
00599         retval = getaddrinfo(inhost, NULL, &hints, &addrs);
00600         if (retval) {
00601                 printerr(1, "%s while getting full hostname for '%s'\n",
00602                          gai_strerror(retval), inhost);
00603                 goto out;
00604         }
00605         strncpy(outhost, addrs->ai_canonname, outhostlen);
00606         freeaddrinfo(addrs);
00607         for (c = outhost; *c != '\0'; c++)
00608             *c = tolower(*c);
00609 
00610         printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
00611         retval = 0;
00612 out:
00613         return retval;
00614 }
00615 
00616 /* 
00617  * If principal matches the given realm and service name,
00618  * and has *any* instance (hostname), return 1.
00619  * Otherwise return 0, indicating no match.
00620  */
00621 #ifdef HAVE_KRB5
00622 static int
00623 realm_and_service_match(krb5_principal p, const char *realm, const char *service)
00624 {
00625         /* Must have two components */
00626         if (p->length != 2)
00627                 return 0;
00628 
00629         if ((strlen(realm) == p->realm.length)
00630             && (strncmp(realm, p->realm.data, p->realm.length) == 0)
00631             && (strlen(service) == p->data[0].length)
00632             && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
00633                 return 1;
00634 
00635         return 0;
00636 }
00637 #else
00638 static int
00639 realm_and_service_match(krb5_context context, krb5_principal p,
00640                         const char *realm, const char *service)
00641 {
00642         const char *name, *inst;
00643 
00644         if (p->name.name_string.len != 2)
00645                 return 0;
00646 
00647         name = krb5_principal_get_comp_string(context, p, 0);
00648         inst = krb5_principal_get_comp_string(context, p, 1);
00649         if (name == NULL || inst == NULL)
00650                 return 0;
00651         if ((strcmp(realm, p->realm) == 0)
00652             && (strcmp(service, name) == 0))
00653                 return 1;
00654 
00655         return 0;
00656 }
00657 #endif
00658 
00659 /*
00660  * Search the given keytab file looking for an entry with the given
00661  * service name and realm, ignoring hostname (instance).
00662  *
00663  * Returns:
00664  *      0 => No error
00665  *      non-zero => An error occurred
00666  *
00667  * If a keytab entry is found, "found" is set to one, and the keytab
00668  * entry is returned in "kte".  Otherwise, "found" is zero, and the
00669  * value of "kte" is unpredictable.
00670  */
00671 static int
00672 gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
00673                         const char *realm, const char *service,
00674                         int *found, krb5_keytab_entry *kte)
00675 {
00676         krb5_kt_cursor cursor;
00677         krb5_error_code code;
00678         struct gssd_k5_kt_princ *ple;
00679         int retval = -1, status;
00680         char kt_name[BUFSIZ];
00681         char *pname;
00682         char *k5err = NULL;
00683 
00684         if (found == NULL) {
00685                 retval = EINVAL;
00686                 goto out;
00687         }
00688         *found = 0;
00689 
00690         /*
00691          * Look through each entry in the keytab file and determine
00692          * if we might want to use it as machine credentials.  If so,
00693          * save info in the global principal list (gssd_k5_kt_princ_list).
00694          */
00695         if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
00696                 k5err = gssd_k5_err_msg(context, code);
00697                 printerr(0, "ERROR: %s attempting to get keytab name\n", k5err);
00698                 retval = code;
00699                 goto out;
00700         }
00701         if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
00702                 k5err = gssd_k5_err_msg(context, code);
00703                 printerr(0, "ERROR: %s while beginning keytab scan "
00704                             "for keytab '%s'\n", k5err, kt_name);
00705                 retval = code;
00706                 goto out;
00707         }
00708 
00709         while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
00710                 if ((code = krb5_unparse_name(context, kte->principal,
00711                                               &pname))) {
00712                         k5err = gssd_k5_err_msg(context, code);
00713                         printerr(0, "WARNING: Skipping keytab entry because "
00714                                  "we failed to unparse principal name: %s\n",
00715                                  k5err);
00716                         k5_free_kt_entry(context, kte);
00717                         continue;
00718                 }
00719                 printerr(4, "Processing keytab entry for principal '%s'\n",
00720                          pname);
00721                 /* Use the first matching keytab entry found */
00722 #ifdef HAVE_KRB5
00723                 status = realm_and_service_match(kte->principal, realm, service);
00724 #else
00725                 status = realm_and_service_match(context, kte->principal, realm, service);
00726 #endif
00727                 if (status) {
00728                         printerr(4, "We WILL use this entry (%s)\n", pname);
00729                         ple = get_ple_by_princ(context, kte->principal);
00730                         /*
00731                          * Return, don't free, keytab entry if
00732                          * we were successful!
00733                          */
00734                         if (ple == NULL) {
00735                                 retval = ENOMEM;
00736                                 k5_free_kt_entry(context, kte);
00737                         } else {
00738                                 retval = 0;
00739                                 *found = 1;
00740                         }
00741                         k5_free_unparsed_name(context, pname);
00742                         break;
00743                 }
00744                 else {
00745                         printerr(4, "We will NOT use this entry (%s)\n",
00746                                 pname);
00747                 }
00748                 k5_free_unparsed_name(context, pname);
00749                 k5_free_kt_entry(context, kte);
00750         }
00751 
00752         if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
00753                 k5err = gssd_k5_err_msg(context, code);
00754                 printerr(0, "WARNING: %s while ending keytab scan for "
00755                             "keytab '%s'\n", k5err, kt_name);
00756         }
00757 
00758         retval = 0;
00759   out:
00760         free(k5err);
00761         return retval;
00762 }
00763 
00764 /*
00765  * Find a keytab entry to use for a given target hostname.
00766  * Tries to find the most appropriate keytab to use given the
00767  * name of the host we are trying to connect with.
00768  */
00769 static int
00770 find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
00771                   krb5_keytab_entry *kte, const char **svcnames)
00772 {
00773         krb5_error_code code;
00774         char **realmnames = NULL;
00775         char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
00776         char myhostad[NI_MAXHOST+1];
00777         int i, j, retval;
00778         char *default_realm = NULL;
00779         char *realm;
00780         char *k5err = NULL;
00781         int tried_all = 0, tried_default = 0;
00782         krb5_principal princ;
00783 
00784 
00785         /* Get full target hostname */
00786         retval = get_full_hostname(hostname, targethostname,
00787                                    sizeof(targethostname));
00788         if (retval)
00789                 goto out;
00790 
00791         /* Get full local hostname */
00792         retval = gethostname(myhostname, sizeof(myhostname));
00793         if (retval) {
00794                 k5err = gssd_k5_err_msg(context, retval);
00795                 printerr(1, "%s while getting local hostname\n", k5err);
00796                 goto out;
00797         }
00798 
00799         /* Compute the active directory machine name HOST$ */
00800         strcpy(myhostad, myhostname);
00801         for (i = 0; myhostad[i] != 0; ++i)
00802                 myhostad[i] = toupper(myhostad[i]);
00803         myhostad[i] = '$';
00804         myhostad[i+1] = 0;
00805 
00806         retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
00807         if (retval)
00808                 goto out;
00809 
00810         code = krb5_get_default_realm(context, &default_realm);
00811         if (code) {
00812                 retval = code;
00813                 k5err = gssd_k5_err_msg(context, code);
00814                 printerr(1, "%s while getting default realm name\n", k5err);
00815                 goto out;
00816         }
00817 
00818         /*
00819          * Get the realm name(s) for the target hostname.
00820          * In reality, this function currently only returns a
00821          * single realm, but we code with the assumption that
00822          * someday it may actually return a list.
00823          */
00824         code = krb5_get_host_realm(context, targethostname, &realmnames);
00825         if (code) {
00826                 k5err = gssd_k5_err_msg(context, code);
00827                 printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
00828                          k5err, targethostname);
00829                 retval = code;
00830                 goto out;
00831         }
00832 
00833         /*
00834          * Try the "appropriate" realm first, and if nothing found for that
00835          * realm, try the default realm (if it hasn't already been tried).
00836          */
00837         i = 0;
00838         realm = realmnames[i];
00839         while (1) {
00840                 if (realm == NULL) {
00841                         tried_all = 1;
00842                         if (!tried_default)
00843                                 realm = default_realm;
00844                 }
00845                 if (tried_all && tried_default)
00846                         break;
00847                 if (strcmp(realm, default_realm) == 0)
00848                         tried_default = 1;
00849                 for (j = 0; svcnames[j] != NULL; j++) {
00850                         char spn[300];
00851 
00852                         /*
00853                          * The special svcname "$" means 'try the active
00854                          * directory machine account'
00855                          */
00856                         if (strcmp(svcnames[j],"$") == 0) {
00857                                 snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm);
00858                                 code = krb5_build_principal_ext(context, &princ,
00859                                                                 strlen(realm),
00860                                                                 realm,
00861                                                                 strlen(myhostad),
00862                                                                 myhostad,
00863                                                                 NULL);
00864                         } else {
00865                                 snprintf(spn, sizeof(spn), "%s/%s@%s",
00866                                          svcnames[j], myhostname, realm);
00867                                 code = krb5_build_principal_ext(context, &princ,
00868                                                                 strlen(realm),
00869                                                                 realm,
00870                                                                 strlen(svcnames[j]),
00871                                                                 svcnames[j],
00872                                                                 strlen(myhostname),
00873                                                                 myhostname,
00874                                                                 NULL);
00875                         }
00876 
00877                         if (code) {
00878                                 k5err = gssd_k5_err_msg(context, code);
00879                                 printerr(1, "%s while building principal for '%s'\n",
00880                                          k5err, spn);
00881                                 continue;
00882                         }
00883                         code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
00884                         krb5_free_principal(context, princ);
00885                         if (code) {
00886                                 k5err = gssd_k5_err_msg(context, code);
00887                                 printerr(3, "%s while getting keytab entry for '%s'\n",
00888                                          k5err, spn);
00889                         } else {
00890                                 printerr(3, "Success getting keytab entry for '%s'\n",spn);
00891                                 retval = 0;
00892                                 goto out;
00893                         }
00894                         retval = code;
00895                 }
00896                 /*
00897                  * Nothing found with our hostname instance, now look for
00898                  * names with any instance (they must have an instance)
00899                  */
00900                 for (j = 0; svcnames[j] != NULL; j++) {
00901                         int found = 0;
00902                         if (strcmp(svcnames[j],"$") == 0)
00903                                 continue;
00904                         code = gssd_search_krb5_keytab(context, kt, realm,
00905                                                        svcnames[j], &found, kte);
00906                         if (!code && found) {
00907                                 printerr(3, "Success getting keytab entry for "
00908                                          "%s/*@%s\n", svcnames[j], realm);
00909                                 retval = 0;
00910                                 goto out;
00911                         }
00912                 }
00913                 if (!tried_all) {
00914                         i++;
00915                         realm = realmnames[i];
00916                 }
00917         }
00918 out:
00919         if (default_realm)
00920                 k5_free_default_realm(context, default_realm);
00921         if (realmnames)
00922                 krb5_free_host_realm(context, realmnames);
00923         free(k5err);
00924         return retval;
00925 }
00926 
00927 
00928 static inline int data_is_equal(krb5_data d1, krb5_data d2)
00929 {
00930         return (d1.length == d2.length
00931                 && memcmp(d1.data, d2.data, d1.length) == 0);
00932 }
00933 
00934 static int
00935 check_for_tgt(krb5_context context, krb5_ccache ccache,
00936               krb5_principal principal)
00937 {
00938         krb5_error_code ret;
00939         krb5_creds creds;
00940         krb5_cc_cursor cur;
00941         int found = 0;
00942 
00943         ret = krb5_cc_start_seq_get(context, ccache, &cur);
00944         if (ret) 
00945                 return 0;
00946 
00947         while (!found &&
00948                 (ret = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) {
00949                 if (creds.server->length == 2 &&
00950                                 data_is_equal(creds.server->realm,
00951                                               principal->realm) &&
00952                                 creds.server->data[0].length == 6 &&
00953                                 memcmp(creds.server->data[0].data,
00954                                                 "krbtgt", 6) == 0 &&
00955                                 data_is_equal(creds.server->data[1],
00956                                               principal->realm) &&
00957                                 creds.times.endtime > time(NULL))
00958                         found = 1;
00959                 krb5_free_cred_contents(context, &creds);
00960         }
00961         krb5_cc_end_seq_get(context, ccache, &cur);
00962 
00963         return found;
00964 }
00965 
00966 static int
00967 query_krb5_ccache(const char* cred_cache, char **ret_princname,
00968                   char **ret_realm)
00969 {
00970         krb5_error_code ret;
00971         krb5_context context;
00972         krb5_ccache ccache;
00973         krb5_principal principal;
00974         int found = 0;
00975         char *str = NULL;
00976         char *princstring;
00977 
00978         ret = krb5_init_context(&context);
00979         if (ret) 
00980                 return 0;
00981 
00982         if(!cred_cache || krb5_cc_resolve(context, cred_cache, &ccache))
00983                 goto err_cache;
00984 
00985         if (krb5_cc_set_flags(context, ccache, 0))
00986                 goto err_princ;
00987 
00988         ret = krb5_cc_get_principal(context, ccache, &principal);
00989         if (ret) 
00990                 goto err_princ;
00991 
00992         found = check_for_tgt(context, ccache, principal);
00993         if (found) {
00994                 ret = krb5_unparse_name(context, principal, &princstring);
00995                 if (ret == 0) {
00996                     if ((str = strchr(princstring, '@')) != NULL) {
00997                             *str = '\0';
00998                             *ret_princname = strdup(princstring);
00999                             *ret_realm = strdup(str+1);
01000                     }
01001                     k5_free_unparsed_name(context, princstring);
01002                 } else {
01003                         found = 0;
01004                 }
01005         }
01006         krb5_free_principal(context, principal);
01007 err_princ:
01008         krb5_cc_set_flags(context, ccache,  KRB5_TC_OPENCLOSE);
01009         krb5_cc_close(context, ccache);
01010 err_cache:
01011         krb5_free_context(context);
01012         return found;
01013 }
01014 
01015 /*==========================*/
01016 /*===  External routines ===*/
01017 /*==========================*/
01018 
01019 /*
01020  * Attempt to find the best match for a credentials cache file
01021  * given only a UID.  We really need more information, but we
01022  * do the best we can.
01023  *
01024  * Returns 0 if a ccache was found, and a non-zero error code otherwise.
01025  */
01026 int
01027 gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
01028 {
01029         char                    buf[MAX_NETOBJ_SZ];
01030         struct dirent           *d;
01031         int                     err;
01032 
01033         printerr(2, "getting credentials for client with uid %u for "
01034                     "server %s\n", uid, servername);
01035         memset(buf, 0, sizeof(buf));
01036         err = gssd_find_existing_krb5_ccache(uid, dirname, &d);
01037         if (err)
01038                 return err;
01039 
01040         snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
01041         free(d);
01042 
01043         printerr(2, "using %s as credentials cache for client with "
01044                     "uid %u for server %s\n", buf, uid, servername);
01045         gssd_set_krb5_ccache_name(buf);
01046         return err;
01047 }
01048 
01049 /*
01050  * Let the gss code know where to find the machine credentials ccache.
01051  *
01052  * Returns:
01053  *      void
01054  */
01055 void
01056 gssd_setup_krb5_machine_gss_ccache(char *ccname)
01057 {
01058         printerr(2, "using %s as credentials cache for machine creds\n",
01059                  ccname);
01060         gssd_set_krb5_ccache_name(ccname);
01061 }
01062 
01063 /*
01064  * Return an array of pointers to names of credential cache files
01065  * which can be used to try to create gss contexts with a server.
01066  *
01067  * Returns:
01068  *      0 => list is attached
01069  *      nonzero => error
01070  */
01071 int
01072 gssd_get_krb5_machine_cred_list(char ***list)
01073 {
01074         char **l;
01075         int listinc = 10;
01076         int listsize = listinc;
01077         int i = 0;
01078         int retval;
01079         struct gssd_k5_kt_princ *ple;
01080 
01081         /* Assume failure */
01082         retval = -1;
01083         *list = (char **) NULL;
01084 
01085         pthread_mutex_lock(&ple_mtx);
01086 
01087         if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
01088                 retval = ENOMEM;
01089                 goto out;
01090         }
01091 
01092         for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
01093                 if (ple->ccname) {
01094                         /* Make sure cred is up-to-date before returning it */
01095                         retval = gssd_refresh_krb5_machine_credential(NULL, ple,
01096                                 NULL);
01097                         if (retval)
01098                                 continue;
01099                         if (i + 1 > listsize) {
01100                                 listsize += listinc;
01101                                 l = (char **)
01102                                         realloc(l, listsize * sizeof(char *));
01103                                 if (l == NULL) {
01104                                         retval = ENOMEM;
01105                                         goto out;
01106                                 }
01107                         }
01108                         if ((l[i++] = strdup(ple->ccname)) == NULL) {
01109                                 retval = ENOMEM;
01110                                 goto out;
01111                         }
01112                 }
01113         }
01114         if (i > 0) {
01115                 l[i] = NULL;
01116                 *list = l;
01117                 retval = 0;
01118                 goto out;
01119         }
01120 
01121   out:
01122         pthread_mutex_unlock(&ple_mtx);
01123         return retval;
01124 }
01125 
01126 /*
01127  * Frees the list of names returned in get_krb5_machine_cred_list()
01128  */
01129 void
01130 gssd_free_krb5_machine_cred_list(char **list)
01131 {
01132         char **n;
01133 
01134         if (list == NULL)
01135                 return;
01136         for (n = list; n && *n; n++) {
01137                 free(*n);
01138         }
01139         free(list);
01140 }
01141 
01142 /*
01143  * Called upon exit.  Destroys machine credentials.
01144  */
01145 void
01146 gssd_destroy_krb5_machine_creds(void)
01147 {
01148         krb5_context context;
01149         krb5_error_code code = 0;
01150         krb5_ccache ccache;
01151         struct gssd_k5_kt_princ *ple;
01152         char *k5err = NULL;
01153 
01154         code = krb5_init_context(&context);
01155         if (code) {
01156                 k5err = gssd_k5_err_msg(NULL, code);
01157                 printerr(0, "ERROR: %s while initializing krb5\n", k5err);
01158                 goto out;
01159         }
01160 
01161         for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
01162                 if (!ple->ccname)
01163                         continue;
01164                 if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
01165                         k5err = gssd_k5_err_msg(context, code);
01166                         printerr(0, "WARNING: %s while resolving credential "
01167                                     "cache '%s' for destruction\n", k5err,
01168                                     ple->ccname);
01169                         continue;
01170                 }
01171 
01172                 if ((code = krb5_cc_destroy(context, ccache))) {
01173                         k5err = gssd_k5_err_msg(context, code);
01174                         printerr(0, "WARNING: %s while destroying credential "
01175                                     "cache '%s'\n", k5err, ple->ccname);
01176                 }
01177         }
01178   out:
01179         free(k5err);
01180         krb5_free_context(context);
01181 }
01182 
01183 /*
01184  * Obtain (or refresh if necessary) Kerberos machine credentials
01185  */
01186 int
01187 gssd_refresh_krb5_machine_credential(char *hostname,
01188                                      struct gssd_k5_kt_princ *ple, 
01189                                          char *service)
01190 {
01191         krb5_error_code code = 0;
01192         krb5_context context;
01193         krb5_keytab kt = NULL;;
01194         int retval = 0;
01195         char *k5err = NULL;
01196         const char *svcnames[5] = { "$", "root", "nfs", "host", NULL };
01197 
01198         /*
01199          * If a specific service name was specified, use it.
01200          * Otherwise, use the default list.
01201          */
01202         if (service != NULL && strcmp(service, "*") != 0) {
01203                 svcnames[0] = service;
01204                 svcnames[1] = NULL;
01205         }
01206         if (hostname == NULL && ple == NULL)
01207                 return EINVAL;
01208 
01209         code = krb5_init_context(&context);
01210         if (code) {
01211                 k5err = gssd_k5_err_msg(NULL, code);
01212                 printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
01213                          __func__, k5err);
01214                 retval = code;
01215                 goto out;
01216         }
01217 
01218         if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
01219                 k5err = gssd_k5_err_msg(context, code);
01220                 printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
01221                          __func__, k5err, keytabfile);
01222                 goto out;
01223         }
01224 
01225         if (ple == NULL) {
01226                 krb5_keytab_entry kte;
01227 
01228                 code = find_keytab_entry(context, kt, hostname, &kte, svcnames);
01229                 if (code) {
01230                         printerr(0, "ERROR: %s: no usable keytab entry found "
01231                                  "in keytab %s for connection with host %s\n",
01232                                  __FUNCTION__, keytabfile, hostname);
01233                         retval = code;
01234                         goto out;
01235                 }
01236 
01237                 ple = get_ple_by_princ(context, kte.principal);
01238                 k5_free_kt_entry(context, &kte);
01239                 if (ple == NULL) {
01240                         char *pname;
01241                         if ((krb5_unparse_name(context, kte.principal, &pname))) {
01242                                 pname = NULL;
01243                         }
01244                         printerr(0, "ERROR: %s: Could not locate or create "
01245                                  "ple struct for principal %s for connection "
01246                                  "with host %s\n",
01247                                  __FUNCTION__, pname ? pname : "<unparsable>",
01248                                  hostname);
01249                         if (pname) k5_free_unparsed_name(context, pname);
01250                         goto out;
01251                 }
01252         }
01253         retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
01254 out:
01255         if (kt)
01256                 krb5_kt_close(context, kt);
01257         krb5_free_context(context);
01258         free(k5err);
01259         return retval;
01260 }
01261 
01262 /*
01263  * A common routine for getting the Kerberos error message
01264  */
01265 char *
01266 gssd_k5_err_msg(krb5_context context, krb5_error_code code)
01267 {
01268         const char *origmsg __attribute__((unused));
01269         char *msg = NULL;
01270 
01271 #if HAVE_KRB5_GET_ERROR_MESSAGE
01272         if (context != NULL) {
01273                 origmsg = krb5_get_error_message(context, code);
01274                 msg = strdup(origmsg);
01275                 krb5_free_error_message(context, origmsg);
01276         }
01277 #endif
01278         if (msg != NULL)
01279                 return msg;
01280 #if HAVE_KRB5
01281         return strdup(error_message(code));
01282 #else
01283         if (context != NULL)
01284                 return strdup(krb5_get_err_text(context, code));
01285         else
01286                 return strdup(error_message(code));
01287 #endif
01288 }
01289 
01290 /*
01291  * Return default Kerberos realm
01292  */
01293 void
01294 gssd_k5_get_default_realm(char **def_realm)
01295 {
01296         krb5_context context;
01297 
01298         if (krb5_init_context(&context))
01299                 return;
01300 
01301         krb5_get_default_realm(context, def_realm);
01302 
01303         krb5_free_context(context);
01304 }
01305 
01306 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
01307 /*
01308  * this routine obtains a credentials handle via gss_acquire_cred()
01309  * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
01310  * types negotiated.
01311  *
01312  * XXX Should call some function to determine the enctypes supported
01313  * by the kernel. (Only need to do that once!)
01314  *
01315  * Returns:
01316  *      0 => all went well
01317  *     -1 => there was an error
01318  */
01319 
01320 OM_uint32 gss_set_allowable_enctypes(OM_uint32 *min_stat,
01321                                      gss_cred_id_t credh,
01322                                      gss_OID_desc *krb5oid,
01323                                      int num_enctypes,
01324                                      krb5_enctype enctypes[]);
01325 int
01326 limit_krb5_enctypes(struct rpc_gss_sec *sec)
01327 {
01328         u_int maj_stat, min_stat;
01329         gss_cred_id_t credh;
01330         gss_OID_set_desc  desired_mechs;
01331         krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
01332                                     ENCTYPE_DES_CBC_MD5,
01333                                     ENCTYPE_DES_CBC_MD4 };
01334         int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
01335         extern int num_krb5_enctypes;
01336         extern krb5_enctype *krb5_enctypes;
01337 
01338         /* We only care about getting a krb5 cred */
01339         desired_mechs.count = 1;
01340         desired_mechs.elements = &krb5oid;
01341 
01342         maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
01343                                     &desired_mechs, GSS_C_INITIATE,
01344                                     &credh, NULL, NULL);
01345 
01346         if (maj_stat != GSS_S_COMPLETE) {
01347                 if (get_verbosity() > 0)
01348                         pgsserr("gss_acquire_cred",
01349                                 maj_stat, min_stat, &krb5oid);
01350                 return -1;
01351         }
01352 
01353         /*
01354          * If we failed for any reason to produce global
01355          * list of supported enctypes, use local default here.
01356          */
01357         if (krb5_enctypes == NULL)
01358                 maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
01359                                         &krb5oid, num_enctypes, enctypes);
01360         else
01361                 maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
01362                                         &krb5oid, num_krb5_enctypes, krb5_enctypes);
01363 
01364         if (maj_stat != GSS_S_COMPLETE) {
01365                 pgsserr("gss_set_allowable_enctypes",
01366                         maj_stat, min_stat, &krb5oid);
01367                 gss_release_cred(&min_stat, &credh);
01368                 return -1;
01369         }
01370         sec->cred = credh;
01371 
01372         return 0;
01373 }
01374 #endif  /* HAVE_SET_ALLOWABLE_ENCTYPES */