nfs-ganesha 1.4
|
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 */