2 * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
3 * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
5 * Copyright (c) 2002-2004 The Regents of the University of Michigan.
8 * Andy Adamson <andros@umich.edu>
9 * J. Bruce Fields <bfields@umich.edu>
10 * Marius Aamodt Eriksen <marius@umich.edu>
11 * Kevin Coffman <kwc@umich.edu>
17 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
18 * All Rights Reserved.
20 * Export of this software from the United States of America may
21 * require a specific license from the United States Government.
22 * It is the responsibility of any person or organization contemplating
23 * export to obtain such a license before exporting.
25 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
26 * distribute this software and its documentation for any purpose and
27 * without fee is hereby granted, provided that the above copyright
28 * notice appear in all copies and that both that copyright notice and
29 * this permission notice appear in supporting documentation, and that
30 * the name of M.I.T. not be used in advertising or publicity pertaining
31 * to distribution of the software without specific, written prior
32 * permission. Furthermore if you modify this software you must label
33 * your software as modified software and not distribute it in such a
34 * fashion that it might be confused with the original M.I.T. software.
35 * M.I.T. makes no representations about the suitability of
36 * this software for any purpose. It is provided "as is" without express
37 * or implied warranty.
41 * Copyright 1994 by OpenVision Technologies, Inc.
43 * Permission to use, copy, modify, distribute, and sell this software
44 * and its documentation for any purpose is hereby granted without fee,
45 * provided that the above copyright notice appears in all copies and
46 * that both that copyright notice and this permission notice appear in
47 * supporting documentation, and that the name of OpenVision not be used
48 * in advertising or publicity pertaining to distribution of the software
49 * without specific, written prior permission. OpenVision makes no
50 * representations about the suitability of this software for any
51 * purpose. It is provided "as is" without express or implied warranty.
53 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
54 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
55 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
56 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
57 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
58 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
59 * PERFORMANCE OF THIS SOFTWARE.
64 Copyright (c) 2004 The Regents of the University of Michigan.
67 Redistribution and use in source and binary forms, with or without
68 modification, are permitted provided that the following conditions
71 1. Redistributions of source code must retain the above copyright
72 notice, this list of conditions and the following disclaimer.
73 2. Redistributions in binary form must reproduce the above copyright
74 notice, this list of conditions and the following disclaimer in the
75 documentation and/or other materials provided with the distribution.
76 3. Neither the name of the University nor the names of its
77 contributors may be used to endorse or promote products derived
78 from this software without specific prior written permission.
80 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
81 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
82 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
83 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
84 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
85 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
86 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
87 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
88 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
89 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
90 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
98 #include <sys/param.h>
100 #include <sys/stat.h>
101 #include <sys/socket.h>
102 #include <arpa/inet.h>
113 #include <gssapi/gssapi.h>
114 #ifdef USE_PRIVATE_KRB5_FUNCTIONS
115 #include <gssapi/gssapi_krb5.h>
118 #include <rpc/auth_gss.h>
121 #include "err_util.h"
122 #include "gss_util.h"
123 #include "gss_oids.h"
124 #include "krb5_util.h"
126 /* Global list of principals/cache file names for machine credentials */
127 struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
129 /*==========================*/
130 /*=== Internal routines ===*/
131 /*==========================*/
133 static int select_krb5_ccache(const struct dirent *d);
134 static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
136 static int gssd_get_single_krb5_cred(krb5_context context,
137 krb5_keytab kt, struct gssd_k5_kt_princ *ple);
141 * Called from the scandir function to weed out potential krb5
142 * credentials cache files
145 * 0 => don't select this one
146 * 1 => select this one
149 select_krb5_ccache(const struct dirent *d)
152 * Note: We used to check d->d_type for DT_REG here,
153 * but apparenlty reiser4 always has DT_UNKNOWN.
154 * Check for IS_REG after stat() call instead.
156 if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
163 * Look in directory "dirname" for files that look like they
164 * are Kerberos Credential Cache files for a given UID. Return
165 * non-zero and the dirent pointer for the entry most likely to be
166 * what we want. Otherwise, return zero and no dirent pointer.
167 * The caller is responsible for freeing the dirent if one is returned.
170 * 0 => could not find an existing entry
171 * 1 => found an existing entry
174 gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
176 struct dirent **namelist;
180 struct dirent *best_match_dir = NULL;
181 struct stat best_match_stat, tmp_stat;
183 memset(&best_match_stat, 0, sizeof(best_match_stat));
185 n = scandir(dirname, &namelist, select_krb5_ccache, 0);
187 printerr(1, "Error doing scandir on directory '%s': %s\n",
188 dirname, strerror(errno));
192 for (i = 0; i < n; i++) {
193 printerr(3, "CC file '%s' being considered\n",
194 namelist[i]->d_name);
195 snprintf(statname, sizeof(statname),
196 "%s/%s", dirname, namelist[i]->d_name);
197 if (lstat(statname, &tmp_stat)) {
198 printerr(0, "Error doing stat on file '%s'\n",
203 /* Only pick caches owned by the user (uid) */
204 if (tmp_stat.st_uid != uid) {
205 printerr(3, "'%s' owned by %u, not %u\n",
206 statname, tmp_stat.st_uid, uid);
210 if (!S_ISREG(tmp_stat.st_mode)) {
211 printerr(3, "'%s' is not a regular file\n",
216 printerr(3, "CC file '%s' matches owner check and has "
218 namelist[i]->d_name, tmp_stat.st_mtime);
220 * if more than one match is found, return the most
221 * recent (the one with the latest mtime), and
222 * don't free the dirent
225 best_match_dir = namelist[i];
226 best_match_stat = tmp_stat;
231 * If the current match has an mtime later
232 * than the one we are looking at, then use
233 * the current match. Otherwise, we still
234 * have the best match.
236 if (tmp_stat.st_mtime >
237 best_match_stat.st_mtime) {
238 free(best_match_dir);
239 best_match_dir = namelist[i];
240 best_match_stat = tmp_stat;
245 printerr(3, "CC file '%s' is our "
246 "current best match "
247 "with mtime of %u\n",
248 best_match_dir->d_name,
249 best_match_stat.st_mtime);
262 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
264 * this routine obtains a credentials handle via gss_acquire_cred()
265 * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
268 * XXX Should call some function to determine the enctypes supported
269 * by the kernel. (Only need to do that once!)
273 * -1 => there was an error
277 limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
279 u_int maj_stat, min_stat;
281 gss_OID_set_desc desired_mechs;
282 krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
284 ENCTYPE_DES_CBC_MD4 };
285 int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
287 /* We only care about getting a krb5 cred */
288 desired_mechs.count = 1;
289 desired_mechs.elements = &krb5oid;
291 maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
292 &desired_mechs, GSS_C_INITIATE,
295 if (maj_stat != GSS_S_COMPLETE) {
296 if (get_verbosity() > 0)
297 pgsserr("gss_acquire_cred",
298 maj_stat, min_stat, &krb5oid);
302 maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
303 num_enctypes, &enctypes);
304 if (maj_stat != GSS_S_COMPLETE) {
305 pgsserr("gss_set_allowable_enctypes",
306 maj_stat, min_stat, &krb5oid);
307 gss_release_cred(&min_stat, &credh);
314 #endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
317 * Obtain credentials via a key in the keytab given
318 * a keytab handle and a gssd_k5_kt_princ structure.
319 * Checks to see if current credentials are expired,
320 * if not, uses the keytab to obtain new credentials.
323 * 0 => success (or credentials have not expired)
327 gssd_get_single_krb5_cred(krb5_context context,
329 struct gssd_k5_kt_princ *ple)
331 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
332 krb5_get_init_creds_opt *init_opts = NULL;
334 krb5_get_init_creds_opt options;
336 krb5_get_init_creds_opt *opts;
338 krb5_ccache ccache = NULL;
339 char kt_name[BUFSIZ];
340 char cc_name[BUFSIZ];
342 time_t now = time(0);
346 memset(&my_creds, 0, sizeof(my_creds));
348 if (ple->ccname && ple->endtime > now) {
349 printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
350 ple->ccname, ple->endtime);
355 if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
356 printerr(0, "ERROR: Unable to get keytab name in "
357 "gssd_get_single_krb5_cred\n");
361 if ((krb5_unparse_name(context, ple->princ, &pname)))
364 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
365 code = krb5_get_init_creds_opt_alloc(context, &init_opts);
367 printerr(0, "ERROR: %s allocating gic options\n",
368 gssd_k5_err_msg(context, code));
371 if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
372 printerr(0, "WARNING: Unable to set option for addressless "
373 "tickets. May have problems behind a NAT.\n");
374 #ifdef TEST_SHORT_LIFETIME
375 /* set a short lifetime (for debugging only!) */
376 printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
377 krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
381 #else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */
383 krb5_get_init_creds_opt_init(&options);
384 krb5_get_init_creds_opt_set_address_list(&options, NULL);
385 #ifdef TEST_SHORT_LIFETIME
386 /* set a short lifetime (for debugging only!) */
387 printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
388 krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
393 if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
394 kt, 0, NULL, opts))) {
395 printerr(0, "WARNING: %s while getting initial ticket for "
396 "principal '%s' using keytab '%s'\n",
397 gssd_k5_err_msg(context, code),
398 pname ? pname : "<unparsable>", kt_name);
403 * Initialize cache file which we're going to be using
407 cache_type = "MEMORY";
410 snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
412 ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
413 GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
414 ple->endtime = my_creds.times.endtime;
415 if (ple->ccname != NULL)
417 ple->ccname = strdup(cc_name);
418 if (ple->ccname == NULL) {
419 printerr(0, "ERROR: no storage to duplicate credentials "
420 "cache name '%s'\n", cc_name);
424 if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
425 printerr(0, "ERROR: %s while opening credential cache '%s'\n",
426 gssd_k5_err_msg(context, code), cc_name);
429 if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
430 printerr(0, "ERROR: %s while initializing credential "
431 "cache '%s'\n", gssd_k5_err_msg(context, code),
435 if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
436 printerr(0, "ERROR: %s while storing credentials in '%s'\n",
437 gssd_k5_err_msg(context, code), cc_name);
442 printerr(2, "Successfully obtained machine credentials for "
443 "principal '%s' stored in ccache '%s'\n", pname, cc_name);
445 #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
447 krb5_get_init_creds_opt_free(context, init_opts);
450 k5_free_unparsed_name(context, pname);
452 krb5_cc_close(context, ccache);
453 krb5_free_cred_contents(context, &my_creds);
458 * Depending on the version of Kerberos, we either need to use
459 * a private function, or simply set the environment variable.
462 gssd_set_krb5_ccache_name(char *ccname)
464 #ifdef USE_GSS_KRB5_CCACHE_NAME
465 u_int maj_stat, min_stat;
467 printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
469 maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
470 if (maj_stat != GSS_S_COMPLETE) {
471 printerr(0, "WARNING: gss_krb5_ccache_name with "
472 "name '%s' failed (%s)\n",
473 ccname, error_message(min_stat));
477 * Set the KRB5CCNAME environment variable to tell the krb5 code
478 * which credentials cache to use. (Instead of using the private
479 * function above for which there is no generic gssapi
482 printerr(2, "using environment variable to select krb5 ccache %s\n",
484 setenv("KRB5CCNAME", ccname, 1);
489 * Given a principal, find a matching ple structure
491 static struct gssd_k5_kt_princ *
492 find_ple_by_princ(krb5_context context, krb5_principal princ)
494 struct gssd_k5_kt_princ *ple;
496 for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
497 if (krb5_principal_compare(context, ple->princ, princ))
505 * Create, initialize, and add a new ple structure to the global list
507 static struct gssd_k5_kt_princ *
508 new_ple(krb5_context context, krb5_principal princ)
510 struct gssd_k5_kt_princ *ple = NULL, *p;
511 krb5_error_code code;
513 int is_default_realm = 0;
515 ple = malloc(sizeof(struct gssd_k5_kt_princ));
518 memset(ple, 0, sizeof(*ple));
521 ple->realm = strndup(princ->realm.data,
522 princ->realm.length);
524 ple->realm = strdup(princ->realm);
526 if (ple->realm == NULL)
528 code = krb5_copy_principal(context, princ, &ple->princ);
533 * Add new entry onto the list (if this is the default
534 * realm, always add to the front of the list)
537 code = krb5_get_default_realm(context, &default_realm);
539 if (strcmp(ple->realm, default_realm) == 0)
540 is_default_realm = 1;
541 k5_free_default_realm(context, default_realm);
544 if (is_default_realm) {
545 ple->next = gssd_k5_kt_princ_list;
546 gssd_k5_kt_princ_list = ple;
548 p = gssd_k5_kt_princ_list;
549 while (p != NULL && p->next != NULL)
552 gssd_k5_kt_princ_list = ple;
568 * Given a principal, find an existing ple structure, or create one
570 static struct gssd_k5_kt_princ *
571 get_ple_by_princ(krb5_context context, krb5_principal princ)
573 struct gssd_k5_kt_princ *ple;
575 /* Need to serialize list if we ever become multi-threaded! */
577 ple = find_ple_by_princ(context, princ);
579 ple = new_ple(context, princ);
586 * Given a (possibly unqualified) hostname,
587 * return the fully qualified (lower-case!) hostname
590 get_full_hostname(const char *inhost, char *outhost, int outhostlen)
592 struct addrinfo *addrs = NULL;
593 struct addrinfo hints;
597 memset(&hints, 0, sizeof(hints));
598 hints.ai_socktype = SOCK_STREAM;
599 hints.ai_family = PF_UNSPEC;
600 hints.ai_flags = AI_CANONNAME;
602 /* Get full target hostname */
603 retval = getaddrinfo(inhost, NULL, &hints, &addrs);
605 printerr(0, "%s while getting full hostname for '%s'\n",
606 gai_strerror(retval), inhost);
609 strncpy(outhost, addrs->ai_canonname, outhostlen);
611 for (c = outhost; *c != '\0'; c++)
614 printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
621 * If principal matches the given realm and service name,
622 * and has *any* instance (hostname), return 1.
623 * Otherwise return 0, indicating no match.
626 realm_and_service_match(krb5_context context, krb5_principal p,
627 const char *realm, const char *service)
630 /* Must have two components */
633 if ((strlen(realm) == p->realm.length)
634 && (strncmp(realm, p->realm.data, p->realm.length) == 0)
635 && (strlen(service) == p->data[0].length)
636 && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
639 const char *name, *inst;
641 if (p->name.name_string.len != 2)
643 name = krb5_principal_get_comp_string(context, p, 0);
644 inst = krb5_principal_get_comp_string(context, p, 1);
645 if (name == NULL || inst == NULL)
647 if ((strcmp(realm, p->realm) == 0)
648 && (strcmp(service, name) == 0))
655 * Search the given keytab file looking for an entry with the given
656 * service name and realm, ignoring hostname (instance).
660 * non-zero => An error occurred
662 * If a keytab entry is found, "found" is set to one, and the keytab
663 * entry is returned in "kte". Otherwise, "found" is zero, and the
664 * value of "kte" is unpredictable.
667 gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
668 const char *realm, const char *service,
669 int *found, krb5_keytab_entry *kte)
671 krb5_kt_cursor cursor;
672 krb5_error_code code;
673 struct gssd_k5_kt_princ *ple;
675 char kt_name[BUFSIZ];
685 * Look through each entry in the keytab file and determine
686 * if we might want to use it as machine credentials. If so,
687 * save info in the global principal list (gssd_k5_kt_princ_list).
689 if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
690 printerr(0, "ERROR: %s attempting to get keytab name\n",
691 gssd_k5_err_msg(context, code));
695 if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
696 printerr(0, "ERROR: %s while beginning keytab scan "
698 gssd_k5_err_msg(context, code), kt_name);
703 while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
704 if ((code = krb5_unparse_name(context, kte->principal,
706 printerr(0, "WARNING: Skipping keytab entry because "
707 "we failed to unparse principal name: %s\n",
708 gssd_k5_err_msg(context, code));
709 k5_free_kt_entry(context, kte);
712 printerr(4, "Processing keytab entry for principal '%s'\n",
714 /* Use the first matching keytab entry found */
715 if ((realm_and_service_match(context, kte->principal, realm,
717 printerr(4, "We WILL use this entry (%s)\n", pname);
718 ple = get_ple_by_princ(context, kte->principal);
720 * Return, don't free, keytab entry if
721 * we were successful!
725 k5_free_kt_entry(context, kte);
730 k5_free_unparsed_name(context, pname);
734 printerr(4, "We will NOT use this entry (%s)\n",
737 k5_free_unparsed_name(context, pname);
738 k5_free_kt_entry(context, kte);
741 if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
742 printerr(0, "WARNING: %s while ending keytab scan for "
744 gssd_k5_err_msg(context, code), kt_name);
753 * Find a keytab entry to use for a given target hostname.
754 * Tries to find the most appropriate keytab to use given the
755 * name of the host we are trying to connect with.
758 find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
759 krb5_keytab_entry *kte)
761 krb5_error_code code;
762 const char *svcnames[] = { "root", "nfs", "host", NULL };
763 char **realmnames = NULL;
764 char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
766 char *default_realm = NULL;
768 int tried_all = 0, tried_default = 0;
769 krb5_principal princ;
772 /* Get full target hostname */
773 retval = get_full_hostname(hostname, targethostname,
774 sizeof(targethostname));
778 /* Get full local hostname */
779 retval = gethostname(myhostname, sizeof(myhostname));
781 printerr(1, "%s while getting local hostname\n",
782 gssd_k5_err_msg(context, retval));
785 retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
789 code = krb5_get_default_realm(context, &default_realm);
792 printerr(1, "%s while getting default realm name\n",
793 gssd_k5_err_msg(context, code));
798 * Get the realm name(s) for the target hostname.
799 * In reality, this function currently only returns a
800 * single realm, but we code with the assumption that
801 * someday it may actually return a list.
803 code = krb5_get_host_realm(context, targethostname, &realmnames);
805 printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
806 gssd_k5_err_msg(context, code), targethostname);
812 * Try the "appropriate" realm first, and if nothing found for that
813 * realm, try the default realm (if it hasn't already been tried).
816 realm = realmnames[i];
821 realm = default_realm;
823 if (tried_all && tried_default)
825 if (strcmp(realm, default_realm) == 0)
827 for (j = 0; svcnames[j] != NULL; j++) {
828 code = krb5_build_principal_ext(context, &princ,
837 printerr(1, "%s while building principal for "
839 gssd_k5_err_msg(context, code),
840 svcnames[j], myhostname, realm);
843 code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
844 krb5_free_principal(context, princ);
846 printerr(3, "%s while getting keytab entry for "
848 gssd_k5_err_msg(context, code),
849 svcnames[j], myhostname, realm);
851 printerr(3, "Success getting keytab entry for "
853 svcnames[j], myhostname, realm);
860 * Nothing found with our hostname instance, now look for
861 * names with any instance (they must have an instance)
863 for (j = 0; svcnames[j] != NULL; j++) {
865 code = gssd_search_krb5_keytab(context, kt, realm,
866 svcnames[j], &found, kte);
867 if (!code && found) {
868 printerr(3, "Success getting keytab entry for "
869 "%s/*@%s\n", svcnames[j], realm);
876 realm = realmnames[i];
881 k5_free_default_realm(context, default_realm);
883 krb5_free_host_realm(context, realmnames);
887 /*==========================*/
888 /*=== External routines ===*/
889 /*==========================*/
892 * Attempt to find the best match for a credentials cache file
893 * given only a UID. We really need more information, but we
894 * do the best we can.
897 * 0 => a ccache was found
898 * 1 => no ccache was found
901 gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
903 char buf[MAX_NETOBJ_SZ];
906 printerr(2, "getting credentials for client with uid %u for "
907 "server %s\n", uid, servername);
908 memset(buf, 0, sizeof(buf));
909 if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) {
910 snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
915 printerr(2, "using %s as credentials cache for client with "
916 "uid %u for server %s\n", buf, uid, servername);
917 gssd_set_krb5_ccache_name(buf);
922 * Let the gss code know where to find the machine credentials ccache.
928 gssd_setup_krb5_machine_gss_ccache(char *ccname)
930 printerr(2, "using %s as credentials cache for machine creds\n",
932 gssd_set_krb5_ccache_name(ccname);
936 * Return an array of pointers to names of credential cache files
937 * which can be used to try to create gss contexts with a server.
940 * 0 => list is attached
944 gssd_get_krb5_machine_cred_list(char ***list)
948 int listsize = listinc;
951 struct gssd_k5_kt_princ *ple;
955 *list = (char **) NULL;
957 if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
962 /* Need to serialize list if we ever become multi-threaded! */
964 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
966 /* Make sure cred is up-to-date before returning it */
967 retval = gssd_refresh_krb5_machine_credential(NULL, ple);
970 if (i + 1 > listsize) {
973 realloc(l, listsize * sizeof(char *));
979 if ((l[i++] = strdup(ple->ccname)) == NULL) {
996 * Frees the list of names returned in get_krb5_machine_cred_list()
999 gssd_free_krb5_machine_cred_list(char **list)
1005 for (n = list; n && *n; n++) {
1012 * Called upon exit. Destroys machine credentials.
1015 gssd_destroy_krb5_machine_creds(void)
1017 krb5_context context;
1018 krb5_error_code code = 0;
1020 struct gssd_k5_kt_princ *ple;
1022 code = krb5_init_context(&context);
1024 printerr(0, "ERROR: %s while initializing krb5\n",
1025 gssd_k5_err_msg(NULL, code));
1029 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
1032 if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
1033 printerr(0, "WARNING: %s while resolving credential "
1034 "cache '%s' for destruction\n",
1035 gssd_k5_err_msg(context, code), ple->ccname);
1039 if ((code = krb5_cc_destroy(context, ccache))) {
1040 printerr(0, "WARNING: %s while destroying credential "
1042 gssd_k5_err_msg(context, code), ple->ccname);
1046 krb5_free_context(context);
1050 * Obtain (or refresh if necessary) Kerberos machine credentials
1053 gssd_refresh_krb5_machine_credential(char *hostname,
1054 struct gssd_k5_kt_princ *ple)
1056 krb5_error_code code = 0;
1057 krb5_context context;
1058 krb5_keytab kt = NULL;;
1061 if (hostname == NULL && ple == NULL)
1064 code = krb5_init_context(&context);
1066 printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
1067 __FUNCTION__, gssd_k5_err_msg(NULL, code));
1072 if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
1073 printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
1074 __FUNCTION__, gssd_k5_err_msg(context, code),
1080 krb5_keytab_entry kte;
1082 code = find_keytab_entry(context, kt, hostname, &kte);
1084 printerr(0, "ERROR: %s: no usable keytab entry found "
1085 "in keytab %s for connection with host %s\n",
1086 __FUNCTION__, keytabfile, hostname);
1091 ple = get_ple_by_princ(context, kte.principal);
1092 k5_free_kt_entry(context, &kte);
1095 if ((krb5_unparse_name(context, kte.principal, &pname))) {
1098 printerr(0, "ERROR: %s: Could not locate or create "
1099 "ple struct for principal %s for connection "
1101 __FUNCTION__, pname ? pname : "<unparsable>",
1103 if (pname) k5_free_unparsed_name(context, pname);
1107 retval = gssd_get_single_krb5_cred(context, kt, ple);
1110 krb5_kt_close(context, kt);
1111 krb5_free_context(context);
1116 * A common routine for getting the Kerberos error message
1119 gssd_k5_err_msg(krb5_context context, krb5_error_code code)
1121 const char *msg = NULL;
1122 #if HAVE_KRB5_GET_ERROR_MESSAGE
1123 if (context != NULL)
1124 msg = krb5_get_error_message(context, code);
1129 return error_message(code);
1131 if (context != NULL)
1132 return krb5_get_err_text(context, code);
1134 return error_message(code);