/* * Create, initialize, and add a new ple structure to the global list */ static struct gssd_k5_kt_princ * new_ple(krb5_context context, krb5_principal princ) { struct gssd_k5_kt_princ *ple = NULL, *p; krb5_error_code code; char *default_realm; int is_default_realm = 0; ple = malloc(sizeof(struct gssd_k5_kt_princ)); if (ple == NULL) goto outerr; memset(ple, 0, sizeof(*ple)); #ifdef HAVE_KRB5 ple->realm = strndup(princ->realm.data, princ->realm.length); #else ple->realm = strdup(princ->realm); #endif if (ple->realm == NULL) goto outerr; code = krb5_copy_principal(context, princ, &ple->princ); if (code) goto outerr; /* * Add new entry onto the list (if this is the default * realm, always add to the front of the list) */ code = krb5_get_default_realm(context, &default_realm); if (code == 0) { if (strcmp(ple->realm, default_realm) == 0) is_default_realm = 1; k5_free_default_realm(context, default_realm); } if (is_default_realm) { ple->next = gssd_k5_kt_princ_list; gssd_k5_kt_princ_list = ple; } else { p = gssd_k5_kt_princ_list; while (p != NULL && p->next != NULL) p = p->next; if (p == NULL) gssd_k5_kt_princ_list = ple; else p->next = ple; } return ple; outerr: if (ple) { if (ple->realm) free(ple->realm); free(ple); } return NULL; }
/* * Find a keytab entry to use for a given target hostname. * Tries to find the most appropriate keytab to use given the * name of the host we are trying to connect with. */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; char myhostad[NI_MAXHOST + 1]; int i, j, retval; char *default_realm = NULL; char *realm; char *k5err = NULL; int tried_all = 0, tried_default = 0; krb5_principal princ; /* Get full target hostname */ retval = get_full_hostname(hostname, targethostname, sizeof(targethostname)); if (retval) goto out; /* Get full local hostname */ retval = gethostname(myhostname, sizeof(myhostname)); if (retval) { k5err = gssd_k5_err_msg(context, retval); printerr(1, "%s while getting local hostname\n", k5err); gsh_free(k5err); goto out; } /* Compute the active directory machine name HOST$ */ strcpy(myhostad, myhostname); for (i = 0; myhostad[i] != 0; ++i) myhostad[i] = toupper(myhostad[i]); myhostad[i] = '$'; myhostad[i + 1] = 0; retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); if (retval) goto out; code = krb5_get_default_realm(context, &default_realm); if (code) { retval = code; k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while getting default realm name\n", k5err); gsh_free(k5err); goto out; } /* * Get the realm name(s) for the target hostname. * In reality, this function currently only returns a * single realm, but we code with the assumption that * someday it may actually return a list. */ code = krb5_get_host_realm(context, targethostname, &realmnames); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n", k5err, targethostname); gsh_free(k5err); retval = code; goto out; } /* * Try the "appropriate" realm first, and if nothing found for that * realm, try the default realm (if it hasn't already been tried). */ i = 0; realm = realmnames[i]; while (1) { if (realm == NULL) { tried_all = 1; if (!tried_default) realm = default_realm; } if (tried_all && tried_default) break; if (strcmp(realm, default_realm) == 0) tried_default = 1; for (j = 0; svcnames[j] != NULL; j++) { char spn[300]; /* * The special svcname "$" means 'try the active * directory machine account' */ if (strcmp(svcnames[j], "$") == 0) { snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(myhostad), myhostad, NULL); } else { snprintf(spn, sizeof(spn), "%s/%s@%s", svcnames[j], myhostname, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(svcnames [j]), svcnames[j], strlen(myhostname), myhostname, NULL); } if (code) { k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while building principal for '%s'\n", k5err, spn); gsh_free(k5err); continue; } code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); krb5_free_principal(context, princ); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(3, "%s while getting keytab entry for '%s'\n", k5err, spn); gsh_free(k5err); } else { printerr(3, "Success getting keytab entry for '%s'\n", spn); retval = 0; goto out; } retval = code; } /* * Nothing found with our hostname instance, now look for * names with any instance (they must have an instance) */ for (j = 0; svcnames[j] != NULL; j++) { int found = 0; if (strcmp(svcnames[j], "$") == 0) continue; code = gssd_search_krb5_keytab(context, kt, realm, svcnames[j], &found, kte); if (!code && found) { printerr(3, "Success getting keytab entry for " "%s/*@%s\n", svcnames[j], realm); retval = 0; goto out; } } if (!tried_all) { i++; realm = realmnames[i]; } } out: if (default_realm) k5_free_default_realm(context, default_realm); if (realmnames) krb5_free_host_realm(context, realmnames); return retval; }