* what we want. Otherwise, return zero and no dirent pointer.
* The caller is responsible for freeing the dirent if one is returned.
*
- * Returns:
- * 0 => could not find an existing entry
- * 1 => found an existing entry
+ * Returns 0 if a valid-looking entry was found and a non-zero error
+ * code otherwise.
*/
static int
gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
char buf[1030];
char *princname = NULL;
char *realm = NULL;
- int score, best_match_score = 0;
+ int score, best_match_score = 0, err = -EACCES;
memset(&best_match_stat, 0, sizeof(best_match_stat));
*d = NULL;
free(namelist[i]);
continue;
}
+ if (uid == 0 && !root_uses_machine_creds &&
+ strstr(namelist[i]->d_name, "_machine_")) {
+ printerr(3, "CC file '%s' not available to root\n",
+ statname);
+ free(namelist[i]);
+ continue;
+ }
if (!query_krb5_ccache(buf, &princname, &realm)) {
printerr(3, "CC file '%s' is expired or corrupt\n",
statname);
free(namelist[i]);
+ err = -EKEYEXPIRED;
continue;
}
}
free(namelist);
}
- if (found)
- {
+ if (found) {
*d = best_match_dir;
+ return 0;
}
- return found;
-}
-
-
-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
-/*
- * this routine obtains a credentials handle via gss_acquire_cred()
- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
- * types negotiated.
- *
- * XXX Should call some function to determine the enctypes supported
- * by the kernel. (Only need to do that once!)
- *
- * Returns:
- * 0 => all went well
- * -1 => there was an error
- */
-
-int
-limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
-{
- u_int maj_stat, min_stat;
- gss_cred_id_t credh;
- gss_OID_set_desc desired_mechs;
- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
- ENCTYPE_DES_CBC_MD5,
- ENCTYPE_DES_CBC_MD4 };
- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
-
- /* We only care about getting a krb5 cred */
- desired_mechs.count = 1;
- desired_mechs.elements = &krb5oid;
-
- maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
- &desired_mechs, GSS_C_INITIATE,
- &credh, NULL, NULL);
-
- if (maj_stat != GSS_S_COMPLETE) {
- if (get_verbosity() > 0)
- pgsserr("gss_acquire_cred",
- maj_stat, min_stat, &krb5oid);
- return -1;
- }
-
- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
- num_enctypes, &enctypes);
- if (maj_stat != GSS_S_COMPLETE) {
- pgsserr("gss_set_allowable_enctypes",
- maj_stat, min_stat, &krb5oid);
- gss_release_cred(&min_stat, &credh);
- return -1;
- }
- sec->cred = credh;
- return 0;
+ return err;
}
-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
/*
* Obtain credentials via a key in the keytab given
* and has *any* instance (hostname), return 1.
* Otherwise return 0, indicating no match.
*/
+#ifdef HAVE_KRB5
static int
-realm_and_service_match(krb5_context context, krb5_principal p,
- const char *realm, const char *service)
+realm_and_service_match(krb5_principal p, const char *realm, const char *service)
{
-#ifdef HAVE_KRB5
/* Must have two components */
if (p->length != 2)
return 0;
+
if ((strlen(realm) == p->realm.length)
&& (strncmp(realm, p->realm.data, p->realm.length) == 0)
&& (strlen(service) == p->data[0].length)
&& (strncmp(service, p->data[0].data, p->data[0].length) == 0))
return 1;
+
+ return 0;
+}
#else
+static int
+realm_and_service_match(krb5_context context, krb5_principal p,
+ const char *realm, const char *service)
+{
const char *name, *inst;
if (p->name.name_string.len != 2)
return 0;
+
name = krb5_principal_get_comp_string(context, p, 0);
inst = krb5_principal_get_comp_string(context, p, 1);
if (name == NULL || inst == NULL)
if ((strcmp(realm, p->realm) == 0)
&& (strcmp(service, name) == 0))
return 1;
-#endif
+
return 0;
}
+#endif
/*
* Search the given keytab file looking for an entry with the given
krb5_kt_cursor cursor;
krb5_error_code code;
struct gssd_k5_kt_princ *ple;
- int retval = -1;
+ int retval = -1, status;
char kt_name[BUFSIZ];
char *pname;
char *k5err = NULL;
printerr(4, "Processing keytab entry for principal '%s'\n",
pname);
/* Use the first matching keytab entry found */
- if ((realm_and_service_match(context, kte->principal, realm,
- service))) {
+#ifdef HAVE_KRB5
+ status = realm_and_service_match(kte->principal, realm, service);
+#else
+ status = realm_and_service_match(context, kte->principal, realm, service);
+#endif
+ if (status) {
printerr(4, "We WILL use this entry (%s)\n", pname);
ple = get_ple_by_princ(context, kte->principal);
/*
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;
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;
if (strcmp(realm, default_realm) == 0)
tried_default = 1;
for (j = 0; svcnames[j] != NULL; j++) {
- code = krb5_build_principal_ext(context, &princ,
- strlen(realm),
- realm,
- strlen(svcnames[j]),
- svcnames[j],
- strlen(myhostname),
- myhostname,
- NULL);
+ 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/%s@%s'\n", k5err, svcnames[j],
- myhostname, realm);
+ 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/%s@%s'\n", k5err, svcnames[j],
- myhostname, realm);
+ printerr(3, "%s while getting keytab entry for '%s'\n",
+ k5err, spn);
} else {
- printerr(3, "Success getting keytab entry for "
- "'%s/%s@%s'\n",
- svcnames[j], myhostname, realm);
+ printerr(3, "Success getting keytab entry for '%s'\n",spn);
retval = 0;
goto out;
}
*/
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) {
* given only a UID. We really need more information, but we
* do the best we can.
*
- * Returns:
- * 0 => a ccache was found
- * 1 => no ccache was found
+ * Returns 0 if a ccache was found, and a non-zero error code otherwise.
*/
int
gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
{
char buf[MAX_NETOBJ_SZ];
struct dirent *d;
+ int err;
printerr(2, "getting credentials for client with uid %u for "
"server %s\n", uid, servername);
memset(buf, 0, sizeof(buf));
- if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) {
- snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
- free(d);
- }
- else
- return 1;
+ err = gssd_find_existing_krb5_ccache(uid, dirname, &d);
+ if (err)
+ return err;
+
+ snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
+ free(d);
+
printerr(2, "using %s as credentials cache for client with "
"uid %u for server %s\n", buf, uid, servername);
gssd_set_krb5_ccache_name(buf);
- return 0;
+ return err;
}
/*
krb5_keytab kt = NULL;;
int retval = 0;
char *k5err = NULL;
- const char *svcnames[4] = { "root", "nfs", "host", NULL };
+ const char *svcnames[5] = { "$", "root", "nfs", "host", NULL };
/*
* If a specific service name was specified, use it.
krb5_free_context(context);
}
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * XXX Should call some function to determine the enctypes supported
+ * by the kernel. (Only need to do that once!)
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec)
+{
+ u_int maj_stat, min_stat;
+ gss_cred_id_t credh;
+ gss_OID_set_desc desired_mechs;
+ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+ extern int num_krb5_enctypes;
+ extern krb5_enctype *krb5_enctypes;
+
+ /* We only care about getting a krb5 cred */
+ desired_mechs.count = 1;
+ desired_mechs.elements = &krb5oid;
+
+ maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
+ &desired_mechs, GSS_C_INITIATE,
+ &credh, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_acquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ /*
+ * If we failed for any reason to produce global
+ * list of supported enctypes, use local default here.
+ */
+ if (krb5_enctypes == NULL)
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_enctypes, enctypes);
+ else
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_krb5_enctypes, krb5_enctypes);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ gss_release_cred(&min_stat, &credh);
+ return -1;
+ }
+ sec->cred = credh;
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */