+ free(k5err);
+ return retval;
+}
+
+/*
+ * 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ } 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);
+ free(k5err);