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, struct dirent **d);
135 static int gssd_get_single_krb5_cred(krb5_context context,
136 krb5_keytab kt, struct gssd_k5_kt_princ *ple);
137 static int gssd_have_realm_ple(void *realm);
138 static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
143 * Called from the scandir function to weed out potential krb5
144 * credentials cache files
147 * 0 => don't select this one
148 * 1 => select this one
151 select_krb5_ccache(const struct dirent *d)
154 * Note: We used to check d->d_type for DT_REG here,
155 * but apparenlty reiser4 always has DT_UNKNOWN.
156 * Check for IS_REG after stat() call instead.
158 if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
165 * Look in the ccachedir for files that look like they
166 * are Kerberos Credential Cache files for a given UID. Return
167 * non-zero and the dirent pointer for the entry most likely to be
168 * what we want. Otherwise, return zero and no dirent pointer.
169 * The caller is responsible for freeing the dirent if one is returned.
172 * 0 => could not find an existing entry
173 * 1 => found an existing entry
176 gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
178 struct dirent **namelist;
182 struct dirent *best_match_dir = NULL;
183 struct stat best_match_stat, tmp_stat;
185 memset(&best_match_stat, 0, sizeof(best_match_stat));
187 n = scandir(ccachedir, &namelist, select_krb5_ccache, 0);
189 perror("scandir looking for krb5 credentials caches");
193 for (i = 0; i < n; i++) {
194 printerr(3, "CC file '%s' being considered\n",
195 namelist[i]->d_name);
196 snprintf(statname, sizeof(statname),
197 "%s/%s", ccachedir, namelist[i]->d_name);
198 if (lstat(statname, &tmp_stat)) {
199 printerr(0, "Error doing stat on file '%s'\n",
204 /* Only pick caches owned by the user (uid) */
205 if (tmp_stat.st_uid != uid) {
206 printerr(3, "'%s' owned by %u, not %u\n",
207 statname, tmp_stat.st_uid, uid);
211 if (!S_ISREG(tmp_stat.st_mode)) {
212 printerr(3, "'%s' is not a regular file\n",
217 printerr(3, "CC file '%s' matches owner check and has "
219 namelist[i]->d_name, tmp_stat.st_mtime);
221 * if more than one match is found, return the most
222 * recent (the one with the latest mtime), and
223 * don't free the dirent
226 best_match_dir = namelist[i];
227 best_match_stat = tmp_stat;
232 * If the current match has an mtime later
233 * than the one we are looking at, then use
234 * the current match. Otherwise, we still
235 * have the best match.
237 if (tmp_stat.st_mtime >
238 best_match_stat.st_mtime) {
239 free(best_match_dir);
240 best_match_dir = namelist[i];
241 best_match_stat = tmp_stat;
246 printerr(3, "CC file '%s' is our "
247 "current best match "
248 "with mtime of %u\n",
249 best_match_dir->d_name,
250 best_match_stat.st_mtime);
263 #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
265 * this routine obtains a credentials handle via gss_acquire_cred()
266 * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
269 * XXX Should call some function to determine the enctypes supported
270 * by the kernel. (Only need to do that once!)
274 * -1 => there was an error
278 limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
280 u_int maj_stat, min_stat;
282 gss_OID_set_desc desired_mechs;
283 krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC };
284 int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
286 /* We only care about getting a krb5 cred */
287 desired_mechs.count = 1;
288 desired_mechs.elements = &krb5oid;
290 maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
291 &desired_mechs, GSS_C_INITIATE,
294 if (maj_stat != GSS_S_COMPLETE) {
295 pgsserr("gss_acquire_cred",
296 maj_stat, min_stat, &krb5oid);
300 maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
301 num_enctypes, &enctypes);
302 if (maj_stat != GSS_S_COMPLETE) {
303 pgsserr("gss_set_allowable_enctypes",
304 maj_stat, min_stat, &krb5oid);
305 gss_release_cred(&min_stat, &credh);
312 #endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
315 * Obtain credentials via a key in the keytab given
316 * a keytab handle and a gssd_k5_kt_princ structure.
317 * Checks to see if current credentials are expired,
318 * if not, uses the keytab to obtain new credentials.
321 * 0 => success (or credentials have not expired)
325 gssd_get_single_krb5_cred(krb5_context context,
327 struct gssd_k5_kt_princ *ple)
329 krb5_get_init_creds_opt options;
331 krb5_ccache ccache = NULL;
332 char kt_name[BUFSIZ];
333 char cc_name[BUFSIZ];
335 time_t now = time(0);
339 memset(&my_creds, 0, sizeof(my_creds));
341 if (ple->ccname && ple->endtime > now) {
342 printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
343 ple->ccname, ple->endtime);
348 if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
349 printerr(0, "ERROR: Unable to get keytab name in "
350 "gssd_get_single_krb5_cred\n");
354 if ((krb5_unparse_name(context, ple->princ, &pname)))
357 krb5_get_init_creds_opt_init(&options);
358 krb5_get_init_creds_opt_set_address_list(&options, NULL);
360 #ifdef TEST_SHORT_LIFETIME
361 /* set a short lifetime (for debugging only!) */
362 printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
363 krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
365 if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
366 kt, 0, NULL, &options))) {
367 printerr(0, "WARNING: %s while getting initial ticket for "
368 "principal '%s' using keytab '%s'\n",
370 pname ? pname : "<unparsable>", kt_name);
375 * Initialize cache file which we're going to be using
379 cache_type = "MEMORY";
382 snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
384 GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
385 GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
386 ple->endtime = my_creds.times.endtime;
387 if (ple->ccname != NULL)
389 ple->ccname = strdup(cc_name);
390 if (ple->ccname == NULL) {
391 printerr(0, "ERROR: no storage to duplicate credentials "
392 "cache name '%s'\n", cc_name);
396 if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
397 printerr(0, "ERROR: %s while opening credential cache '%s'\n",
398 error_message(code), cc_name);
401 if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
402 printerr(0, "ERROR: %s while initializing credential "
403 "cache '%s'\n", error_message(code), cc_name);
406 if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
407 printerr(0, "ERROR: %s while storing credentials in '%s'\n",
408 error_message(code), cc_name);
413 printerr(2, "Successfully obtained machine credentials for "
414 "principal '%s' stored in ccache '%s'\n", pname, cc_name);
417 k5_free_unparsed_name(context, pname);
419 krb5_cc_close(context, ccache);
420 krb5_free_cred_contents(context, &my_creds);
425 * Determine if we already have a ple for the given realm
428 * 0 => no ple found for given realm
429 * 1 => found ple for given realm
432 gssd_have_realm_ple(void *r)
434 struct gssd_k5_kt_princ *ple;
436 krb5_data *realm = (krb5_data *)r;
438 char *realm = (char *)r;
441 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
443 if ((realm->length == strlen(ple->realm)) &&
444 (strncmp(realm->data, ple->realm, realm->length) == 0)) {
446 if (strcmp(realm, ple->realm) == 0) {
455 * Process the given keytab file and create a list of principals we
456 * might use as machine credentials.
463 gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
465 krb5_kt_cursor cursor;
466 krb5_keytab_entry kte;
467 krb5_error_code code;
468 struct gssd_k5_kt_princ *ple;
472 * Look through each entry in the keytab file and determine
473 * if we might want to use it as machine credentials. If so,
474 * save info in the global principal list (gssd_k5_kt_princ_list).
475 * Note: (ple == principal list entry)
477 if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
478 printerr(0, "ERROR: %s while beginning keytab scan "
480 error_message(code), kt_name);
485 while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
487 if ((code = krb5_unparse_name(context, kte.principal,
489 printerr(0, "WARNING: Skipping keytab entry because "
490 "we failed to unparse principal name: %s\n",
491 error_message(code));
492 krb5_kt_free_entry(context, &kte);
495 printerr(2, "Processing keytab entry for principal '%s'\n",
497 /* Just use the first keytab entry found for each realm */
498 if ((!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
499 printerr(2, "We WILL use this entry (%s)\n", pname);
500 ple = malloc(sizeof(struct gssd_k5_kt_princ));
502 printerr(0, "ERROR: could not allocate storage "
503 "for principal list entry\n");
504 k5_free_unparsed_name(context, pname);
505 krb5_kt_free_entry(context, &kte);
509 /* These will be filled in later */
515 strndup(kte.principal->realm.data,
516 kte.principal->realm.length))
518 strdup(kte.principal->realm))
521 printerr(0, "ERROR: %s while copying realm to "
522 "principal list entry\n",
523 "not enough memory");
524 k5_free_unparsed_name(context, pname);
525 krb5_kt_free_entry(context, &kte);
529 if ((code = krb5_copy_principal(context,
530 kte.principal, &ple->princ))) {
531 printerr(0, "ERROR: %s while copying principal "
532 "to principal list entry\n",
533 error_message(code));
534 k5_free_unparsed_name(context, pname);
535 krb5_kt_free_entry(context, &kte);
539 if (gssd_k5_kt_princ_list == NULL)
540 gssd_k5_kt_princ_list = ple;
542 ple->next = gssd_k5_kt_princ_list;
543 gssd_k5_kt_princ_list = ple;
547 printerr(2, "We will NOT use this entry (%s)\n",
550 k5_free_unparsed_name(context, pname);
551 krb5_kt_free_entry(context, &kte);
554 if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
555 printerr(0, "WARNING: %s while ending keytab scan for "
557 error_message(code), kt_name);
566 * Depending on the version of Kerberos, we either need to use
567 * a private function, or simply set the environment variable.
570 gssd_set_krb5_ccache_name(char *ccname)
572 #ifdef USE_GSS_KRB5_CCACHE_NAME
573 u_int maj_stat, min_stat;
575 printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
577 maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
578 if (maj_stat != GSS_S_COMPLETE) {
579 printerr(0, "WARNING: gss_krb5_ccache_name with "
580 "name '%s' failed (%s)\n",
581 ccname, error_message(min_stat));
585 * Set the KRB5CCNAME environment variable to tell the krb5 code
586 * which credentials cache to use. (Instead of using the private
587 * function above for which there is no generic gssapi
590 printerr(2, "using environment variable to select krb5 ccache %s\n",
592 setenv("KRB5CCNAME", ccname, 1);
597 * Given a principal, find a matching ple structure
599 static struct gssd_k5_kt_princ *
600 find_ple_by_princ(krb5_context context, krb5_principal princ)
602 struct gssd_k5_kt_princ *ple;
604 for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
605 if (krb5_principal_compare(context, ple->princ, princ))
613 * Create, initialize, and add a new ple structure to the global list
615 static struct gssd_k5_kt_princ *
616 new_ple(krb5_context context, krb5_principal princ)
618 struct gssd_k5_kt_princ *ple = NULL, *p;
619 krb5_error_code code;
621 int is_default_realm = 0;
623 ple = malloc(sizeof(struct gssd_k5_kt_princ));
626 memset(ple, 0, sizeof(*ple));
629 ple->realm = strndup(princ->realm.data,
630 princ->realm.length);
632 ple->realm = strdup(princ->realm);
634 if (ple->realm == NULL)
636 code = krb5_copy_principal(context, princ, &ple->princ);
641 * Add new entry onto the list (if this is the default
642 * realm, always add to the front of the list)
645 code = krb5_get_default_realm(context, &default_realm);
647 if (strcmp(ple->realm, default_realm) == 0)
648 is_default_realm = 1;
649 k5_free_default_realm(context, default_realm);
652 if (is_default_realm) {
653 ple->next = gssd_k5_kt_princ_list;
654 gssd_k5_kt_princ_list = ple;
656 p = gssd_k5_kt_princ_list;
657 while (p != NULL && p->next != NULL)
660 gssd_k5_kt_princ_list = ple;
676 * Given a principal, find an existing ple structure, or create one
678 static struct gssd_k5_kt_princ *
679 get_ple_by_princ(krb5_context context, krb5_principal princ)
681 struct gssd_k5_kt_princ *ple;
683 /* Need to serialize list if we ever become multi-threaded! */
685 ple = find_ple_by_princ(context, princ);
687 ple = new_ple(context, princ);
694 * Given a (possibly unqualified) hostname,
695 * return the fully qualified (lower-case!) hostname
698 get_full_hostname(const char *inhost, char *outhost, int outhostlen)
700 struct addrinfo *addrs = NULL;
701 struct addrinfo hints;
705 memset(&hints, 0, sizeof(hints));
706 hints.ai_socktype = SOCK_STREAM;
707 hints.ai_family = PF_UNSPEC;
708 hints.ai_flags = AI_CANONNAME;
710 /* Get full target hostname */
711 retval = getaddrinfo(inhost, NULL, &hints, &addrs);
713 printerr(0, "%s while getting full hostname for '%s'\n",
714 gai_strerror(retval), inhost);
717 strncpy(outhost, addrs->ai_canonname, outhostlen);
719 for (c = outhost; *c != '\0'; c++)
722 printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
729 * If principal matches the given realm and service name,
730 * and has *any* instance (hostname), return 1.
731 * Otherwise return 0, indicating no match.
734 realm_and_service_match(krb5_context context, krb5_principal p,
735 const char *realm, const char *service)
738 /* Must have two components */
741 if ((strlen(realm) == p->realm.length)
742 && (strncmp(realm, p->realm.data, p->realm.length) == 0)
743 && (strlen(service) == p->data[0].length)
744 && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
747 const char *name, *inst;
749 if (p->name.name_string.len != 2)
751 name = krb5_principal_get_comp_string(context, p, 0);
752 inst = krb5_principal_get_comp_string(context, p, 1);
753 if (name == NULL || inst == NULL)
755 if ((strcmp(realm, p->realm) == 0)
756 && (strcmp(service, name) == 0))
763 * Search the given keytab file looking for an entry with the given
764 * service name and realm, ignoring hostname (instance).
768 * non-zero => An error occurred
770 * If a keytab entry is found, "found" is set to one, and the keytab
771 * entry is returned in "kte". Otherwise, "found" is zero, and the
772 * value of "kte" is unpredictable.
775 gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
776 const char *realm, const char *service,
777 int *found, krb5_keytab_entry *kte)
779 krb5_kt_cursor cursor;
780 krb5_error_code code;
781 struct gssd_k5_kt_princ *ple;
783 char kt_name[BUFSIZ];
793 * Look through each entry in the keytab file and determine
794 * if we might want to use it as machine credentials. If so,
795 * save info in the global principal list (gssd_k5_kt_princ_list).
797 if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
798 printerr(0, "ERROR: %s attempting to get keytab name\n",
799 error_message(code));
803 if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
804 printerr(0, "ERROR: %s while beginning keytab scan "
806 error_message(code), kt_name);
811 while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
812 if ((code = krb5_unparse_name(context, kte->principal,
814 printerr(0, "WARNING: Skipping keytab entry because "
815 "we failed to unparse principal name: %s\n",
816 error_message(code));
817 k5_free_kt_entry(context, kte);
820 printerr(4, "Processing keytab entry for principal '%s'\n",
822 /* Use the first matching keytab entry found */
823 if ((realm_and_service_match(context, kte->principal, realm,
825 printerr(4, "We WILL use this entry (%s)\n", pname);
826 ple = get_ple_by_princ(context, kte->principal);
828 * Return, don't free, keytab entry if
829 * we were successful!
833 k5_free_kt_entry(context, kte);
838 k5_free_unparsed_name(context, pname);
842 printerr(4, "We will NOT use this entry (%s)\n",
845 k5_free_unparsed_name(context, pname);
846 k5_free_kt_entry(context, kte);
849 if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
850 printerr(0, "WARNING: %s while ending keytab scan for "
852 error_message(code), kt_name);
861 * Find a keytab entry to use for a given target hostname.
862 * Tries to find the most appropriate keytab to use given the
863 * name of the host we are trying to connect with.
866 find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
867 krb5_keytab_entry *kte)
869 krb5_error_code code;
870 const char *svcnames[] = { "root", "nfs", "host", NULL };
871 char **realmnames = NULL;
872 char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
874 char *default_realm = NULL;
876 int tried_all = 0, tried_default = 0;
877 krb5_principal princ;
880 /* Get full target hostname */
881 retval = get_full_hostname(hostname, targethostname,
882 sizeof(targethostname));
886 /* Get full local hostname */
887 retval = gethostname(myhostname, sizeof(myhostname));
889 printerr(1, "%s while getting local hostname\n",
890 error_message(retval));
893 retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
897 code = krb5_get_default_realm(context, &default_realm);
900 printerr(1, "%s while getting default realm name\n",
901 error_message(code));
906 * Get the realm name(s) for the target hostname.
907 * In reality, this function currently only returns a
908 * single realm, but we code with the assumption that
909 * someday it may actually return a list.
911 code = krb5_get_host_realm(context, targethostname, &realmnames);
913 printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
914 error_message(code), targethostname);
920 * Try the "appropriate" realm first, and if nothing found for that
921 * realm, try the default realm (if it hasn't already been tried).
924 realm = realmnames[i];
929 realm = default_realm;
931 if (tried_all && tried_default)
933 if (strcmp(realm, default_realm) == 0)
935 for (j = 0; svcnames[j] != NULL; j++) {
936 code = krb5_build_principal_ext(context, &princ,
945 printerr(1, "%s while building principal for "
946 "'%s/%s@%s'\n", error_message(code),
947 svcnames[j], myhostname, realm);
950 code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
951 krb5_free_principal(context, princ);
953 printerr(3, "%s while getting keytab entry for "
954 "'%s/%s@%s'\n", error_message(code),
955 svcnames[j], myhostname, realm);
957 printerr(3, "Success getting keytab entry for "
959 svcnames[j], myhostname, realm);
966 * Nothing found with our hostname instance, now look for
967 * names with any instance (they must have an instance)
969 for (j = 0; svcnames[j] != NULL; j++) {
971 code = gssd_search_krb5_keytab(context, kt, realm,
972 svcnames[j], &found, kte);
973 if (!code && found) {
974 printerr(3, "Success getting keytab entry for "
975 "%s/*@%s\n", svcnames[j], realm);
982 realm = realmnames[i];
987 k5_free_default_realm(context, default_realm);
989 krb5_free_host_realm(context, realmnames);
993 /*==========================*/
994 /*=== External routines ===*/
995 /*==========================*/
998 * Attempt to find the best match for a credentials cache file
999 * given only a UID. We really need more information, but we
1000 * do the best we can.
1006 gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
1008 char buf[MAX_NETOBJ_SZ];
1011 printerr(2, "getting credentials for client with uid %u for "
1012 "server %s\n", uid, servername);
1013 memset(buf, 0, sizeof(buf));
1014 if (gssd_find_existing_krb5_ccache(uid, &d)) {
1015 snprintf(buf, sizeof(buf), "FILE:%s/%s",
1016 ccachedir, d->d_name);
1020 snprintf(buf, sizeof(buf), "FILE:%s/%s%u",
1021 ccachedir, GSSD_DEFAULT_CRED_PREFIX, uid);
1022 printerr(2, "using %s as credentials cache for client with "
1023 "uid %u for server %s\n", buf, uid, servername);
1024 gssd_set_krb5_ccache_name(buf);
1028 * Let the gss code know where to find the machine credentials ccache.
1034 gssd_setup_krb5_machine_gss_ccache(char *ccname)
1036 printerr(2, "using %s as credentials cache for machine creds\n",
1038 gssd_set_krb5_ccache_name(ccname);
1042 * The first time through this routine, go through the keytab and
1043 * determine which keys we will try to use as machine credentials.
1044 * Every time through this routine, try to obtain credentials using
1045 * the keytab entries selected the first time through.
1048 * 0 => obtained one or more credentials
1054 gssd_refresh_krb5_machine_creds(void)
1056 krb5_context context = NULL;
1057 krb5_keytab kt = NULL;;
1058 krb5_error_code code;
1060 struct gssd_k5_kt_princ *ple;
1062 static int processed_keytab = 0;
1065 code = krb5_init_context(&context);
1067 printerr(0, "ERROR: %s while initializing krb5 in "
1068 "gssd_refresh_krb5_machine_creds\n",
1069 error_message(code));
1074 printerr(1, "Using keytab file '%s'\n", keytabfile);
1076 if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
1077 printerr(0, "ERROR: %s while resolving keytab '%s'\n",
1078 error_message(code), keytabfile);
1082 /* Only go through the keytab file once. Only print messages once. */
1083 if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
1084 processed_keytab = 1;
1085 gssd_process_krb5_keytab(context, kt, keytabfile);
1086 if (gssd_k5_kt_princ_list == NULL) {
1087 printerr(0, "ERROR: No usable keytab entries found in "
1088 "keytab '%s'\n", keytabfile);
1089 printerr(0, "Do you have a valid keytab entry for "
1090 "%s/<your.host>@<YOUR.REALM> in "
1091 "keytab file %s ?\n",
1092 GSSD_SERVICE_NAME, keytabfile);
1093 printerr(0, "Continuing without (machine) credentials "
1094 "- nfs4 mounts with Kerberos will fail\n");
1099 * If we don't have any keytab entries we liked, then we have a problem
1101 if (gssd_k5_kt_princ_list == NULL) {
1107 * Now go through the list of saved entries and get initial
1108 * credentials for them (We can't do this while making the
1109 * list because it messes up the keytab iteration cursor
1110 * when we use the keytab to get credentials.)
1112 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
1113 if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
1118 printerr(0, "ERROR: No usable machine credentials obtained\n");
1124 if (kt) krb5_kt_close(context, kt);
1125 krb5_free_context(context);
1132 * Return an array of pointers to names of credential cache files
1133 * which can be used to try to create gss contexts with a server.
1136 * 0 => list is attached
1140 gssd_get_krb5_machine_cred_list(char ***list)
1144 int listsize = listinc;
1147 struct gssd_k5_kt_princ *ple;
1149 /* Assume failure */
1151 *list = (char **) NULL;
1153 if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
1158 /* Need to serialize list if we ever become multi-threaded! */
1160 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
1162 /* Make sure cred is up-to-date before returning it */
1163 retval = gssd_refresh_krb5_machine_credential(NULL, ple);
1166 if (i + 1 > listsize) {
1167 listsize += listinc;
1169 realloc(l, listsize * sizeof(char *));
1175 if ((l[i++] = strdup(ple->ccname)) == NULL) {
1192 * Frees the list of names returned in get_krb5_machine_cred_list()
1195 gssd_free_krb5_machine_cred_list(char **list)
1201 for (n = list; n && *n; n++) {
1208 * Called upon exit. Destroys machine credentials.
1211 gssd_destroy_krb5_machine_creds(void)
1213 krb5_context context;
1214 krb5_error_code code = 0;
1216 struct gssd_k5_kt_princ *ple;
1218 code = krb5_init_context(&context);
1220 printerr(0, "ERROR: %s while initializing krb5\n",
1221 error_message(code));
1225 for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
1228 if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
1229 printerr(0, "WARNING: %s while resolving credential "
1230 "cache '%s' for destruction\n",
1231 error_message(code), ple->ccname);
1235 if ((code = krb5_cc_destroy(context, ccache))) {
1236 printerr(0, "WARNING: %s while destroying credential "
1238 error_message(code), ple->ccname);
1242 krb5_free_context(context);
1246 * Obtain (or refresh if necessary) Kerberos machine credentials
1249 gssd_refresh_krb5_machine_credential(char *hostname,
1250 struct gssd_k5_kt_princ *ple)
1252 krb5_error_code code = 0;
1253 krb5_context context;
1254 krb5_keytab kt = NULL;;
1257 if (hostname == NULL && ple == NULL)
1260 code = krb5_init_context(&context);
1262 printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
1263 __FUNCTION__, error_message(code));
1268 if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
1269 printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
1270 __FUNCTION__, error_message(code), keytabfile);
1275 krb5_keytab_entry kte;
1277 code = find_keytab_entry(context, kt, hostname, &kte);
1279 printerr(0, "ERROR: %s: no usable keytab entry found "
1280 "in keytab %s for connection with host %s\n",
1281 __FUNCTION__, keytabfile, hostname);
1286 ple = get_ple_by_princ(context, kte.principal);
1287 k5_free_kt_entry(context, &kte);
1290 if ((krb5_unparse_name(context, kte.principal, &pname))) {
1293 printerr(0, "ERROR: %s: Could not locate or create "
1294 "ple struct for principal %s for connection "
1296 __FUNCTION__, pname ? pname : "<unparsable>",
1298 if (pname) k5_free_unparsed_name(context, pname);
1302 retval = gssd_get_single_krb5_cred(context, kt, ple);
1305 krb5_kt_close(context, kt);
1306 krb5_free_context(context);