Use the certificate subject base in IPA when requesting certs in certmonger.

When using the dogtag CA we can control what the subject of an issued
certificate is regardless of what is in the CSR, we just use the CN value.
The selfsign CA does not have this capability. The subject format must
match the configured format or certificate requests are rejected.

The default format is CN=%s,O=IPA. certmonger by default issues requests
with just CN so all requests would fail if using the selfsign CA.

This subject base is stored in cn=ipaconfig so we can just fetch that
value in the enrollment process and pass it to certmonger to request
the right thing.

Note that this also fixes ipa-join to work with the new argument passing
mechanism.
This commit is contained in:
Rob Crittenden 2010-04-05 16:27:46 -04:00 committed by Jason Gerard DeRose
parent 7c61663def
commit 1d635090cb
4 changed files with 103 additions and 9 deletions

View File

@ -301,7 +301,7 @@ def configure_krb5_conf(fstore, cli_basedn, cli_realm, cli_domain, cli_server, d
return 0 return 0
def configure_certmonger(fstore, options): def configure_certmonger(fstore, subject_base, cli_realm, options):
started = True started = True
try: try:
@ -319,8 +319,10 @@ def configure_certmonger(fstore, options):
# Request our host cert # Request our host cert
if started: if started:
subject = 'CN=%s,%s' % (socket.getfqdn(), subject_base)
principal = 'host/%s@%s' % (socket.getfqdn(), cli_realm)
try: try:
run(["ipa-getcert", "request", "-d", "/etc/pki/nssdb", "-n", "Server-Cert"]) run(["ipa-getcert", "request", "-d", "/etc/pki/nssdb", "-n", "Server-Cert", "-N", subject, "-K", principal])
except: except:
print "certmonger request for host certificate failed" print "certmonger request for host certificate failed"
@ -370,6 +372,8 @@ def main():
cli_realm = None cli_realm = None
cli_basedn = None cli_basedn = None
subject_base = "O=IPA"
if options.unattended and (options.password is None and options.principal is None and options.prompt_password is False) and not options.on_master: if options.unattended and (options.password is None and options.principal is None and options.prompt_password is False) and not options.on_master:
print "One of password and principal are required." print "One of password and principal are required."
return 1 return 1
@ -489,6 +493,13 @@ def main():
if not options.force: if not options.force:
return 1 return 1
print " Use ipa-getkeytab to obtain a host principal for this server." print " Use ipa-getkeytab to obtain a host principal for this server."
start = stderr.find('Certificate subject base is: ')
if start >= 0:
start = start + 29
subject_base = stderr[start:]
subject_base = subject_base.strip()
finally: finally:
if options.principal is not None: if options.principal is not None:
(stderr, stdout, returncode) = run(["/usr/kerberos/bin/kdestroy"], raiseonerr=False) (stderr, stdout, returncode) = run(["/usr/kerberos/bin/kdestroy"], raiseonerr=False)
@ -511,7 +522,7 @@ def main():
print "Configured /etc/ldap.conf" print "Configured /etc/ldap.conf"
if not options.on_master: if not options.on_master:
configure_certmonger(fstore, options) configure_certmonger(fstore, subject_base, cli_realm, options)
# If on master assume kerberos is already configured properly. # If on master assume kerberos is already configured properly.
if not options.on_master: if not options.on_master:

View File

@ -261,6 +261,57 @@ done:
return rval; return rval;
} }
static int
get_subject(const char *ipaserver, char *ldap_base, char **subject)
{
LDAP *ld = NULL;
char *attrs[] = {"ipaCertificateSubjectBase", NULL};
char base[LINE_MAX];
LDAPMessage *entry, *res = NULL;
struct berval **ncvals;
int ret, rval = 0;
ld = connect_ldap(ipaserver, NULL, NULL);
if (!ld) {
rval = 14;
goto done;
}
strcpy(base, "cn=ipaconfig,cn=etc,");
strcat(base, ldap_base);
ret = ldap_search_ext_s(ld, base, LDAP_SCOPE_BASE,
"objectclass=*", attrs, 0,
NULL, NULL, NULL, 0, &res);
if (ret != LDAP_SUCCESS) {
fprintf(stderr, "Search for ipaCertificateSubjectBase failed with error %d",
attrs[0], ret);
rval = 14;
goto done;
}
entry = ldap_first_entry(ld, res);
ncvals = ldap_get_values_len(ld, entry, attrs[0]);
if (!ncvals) {
fprintf(stderr, "No values for %s", attrs[0]);
rval = 14;
goto done;
}
*subject = strdup(ncvals[0]->bv_val);
ldap_value_free_len(ncvals);
done:
if (res) ldap_msgfree(res);
if (ld != NULL) {
ldap_unbind_ext(ld, NULL, NULL);
}
return rval;
}
/* Join a host to the current IPA realm. /* Join a host to the current IPA realm.
* *
* There are several scenarios for this: * There are several scenarios for this:
@ -280,7 +331,7 @@ done:
* the state of the entry. * the state of the entry.
*/ */
static int static int
join_ldap(const char *ipaserver, const char *hostname, const char ** binddn, const char *bindpw, const char ** princ, int quiet) join_ldap(const char *ipaserver, const char *hostname, const char ** binddn, const char *bindpw, const char **princ, const char **subject, int quiet)
{ {
LDAP *ld; LDAP *ld;
char *filter = NULL; char *filter = NULL;
@ -304,6 +355,11 @@ join_ldap(const char *ipaserver, const char *hostname, const char ** binddn, con
goto done; goto done;
} }
if (get_subject(ipaserver, ldap_base, &subject) != 0) {
fprintf(stderr, "Unable to determine certificate subject of %s\n", ipaserver);
/* Not a critical failure */
}
ld = connect_ldap(ipaserver, NULL, NULL); ld = connect_ldap(ipaserver, NULL, NULL);
if (!ld) { if (!ld) {
fprintf(stderr, "Unable to make an LDAP connection to %s\n", ipaserver); fprintf(stderr, "Unable to make an LDAP connection to %s\n", ipaserver);
@ -382,6 +438,7 @@ ldap_done:
free(filter); free(filter);
free(search_base); free(search_base);
free(ldap_base); free(ldap_base);
free(subject);
if (ld != NULL) { if (ld != NULL) {
ldap_unbind_ext(ld, NULL, NULL); ldap_unbind_ext(ld, NULL, NULL);
} }
@ -392,8 +449,9 @@ done:
} }
static int static int
join_krb5(const char *ipaserver, const char *hostname, const char **hostdn, const char **princ, int quiet) { join_krb5(const char *ipaserver, const char *hostname, const char **hostdn, const char **princ, const char **subject, int quiet) {
xmlrpc_env env; xmlrpc_env env;
xmlrpc_value * argArrayP = NULL;
xmlrpc_value * paramArrayP = NULL; xmlrpc_value * paramArrayP = NULL;
xmlrpc_value * paramP = NULL; xmlrpc_value * paramP = NULL;
xmlrpc_value * optionsP = NULL; xmlrpc_value * optionsP = NULL;
@ -403,6 +461,7 @@ join_krb5(const char *ipaserver, const char *hostname, const char **hostdn, cons
struct utsname uinfo; struct utsname uinfo;
xmlrpc_value *princP = NULL; xmlrpc_value *princP = NULL;
xmlrpc_value *krblastpwdchangeP = NULL; xmlrpc_value *krblastpwdchangeP = NULL;
xmlrpc_value *subjectP = NULL;
xmlrpc_value *hostdnP = NULL; xmlrpc_value *hostdnP = NULL;
const char *krblastpwdchange = NULL; const char *krblastpwdchange = NULL;
char * url = NULL; char * url = NULL;
@ -424,17 +483,19 @@ join_krb5(const char *ipaserver, const char *hostname, const char **hostdn, cons
#endif #endif
serverInfoP = xmlrpc_server_info_new(&env, url); serverInfoP = xmlrpc_server_info_new(&env, url);
argArrayP = xmlrpc_array_new(&env);
paramArrayP = xmlrpc_array_new(&env); paramArrayP = xmlrpc_array_new(&env);
if (hostname == NULL) if (hostname == NULL)
paramP = xmlrpc_string_new(&env, uinfo.nodename); paramP = xmlrpc_string_new(&env, uinfo.nodename);
else else
paramP = xmlrpc_string_new(&env, hostname); paramP = xmlrpc_string_new(&env, hostname);
xmlrpc_array_append_item(&env, argArrayP, paramP);
#ifdef REALM #ifdef REALM
if (!quiet) if (!quiet)
printf("Joining %s to IPA realm %s\n", uinfo.nodename, iparealm); printf("Joining %s to IPA realm %s\n", uinfo.nodename, iparealm);
#endif #endif
xmlrpc_array_append_item(&env, paramArrayP, paramP); xmlrpc_array_append_item(&env, paramArrayP, argArrayP);
xmlrpc_DECREF(paramP); xmlrpc_DECREF(paramP);
optionsP = xmlrpc_build_value(&env, "{s:s,s:s}", optionsP = xmlrpc_build_value(&env, "{s:s,s:s}",
@ -488,7 +549,20 @@ join_krb5(const char *ipaserver, const char *hostname, const char **hostdn, cons
goto cleanup; goto cleanup;
} }
xmlrpc_struct_find_value(&env, structP, "ipacertificatesubjectbase", &subjectP);
if (subjectP) {
xmlrpc_value * singleprincP = NULL;
/* FIXME: all values are returned as lists currently. Once this is
* fixed we can read the string directly.
*/
xmlrpc_array_read_item(&env, subjectP, 0, &singleprincP);
xmlrpc_read_string(&env, singleprincP, *&subject);
xmlrpc_DECREF(subjectP);
}
cleanup: cleanup:
if (argArrayP) xmlrpc_DECREF(argArrayP);
if (paramArrayP) xmlrpc_DECREF(paramArrayP); if (paramArrayP) xmlrpc_DECREF(paramArrayP);
if (resultP) xmlrpc_DECREF(resultP); if (resultP) xmlrpc_DECREF(resultP);
@ -513,6 +587,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
char *iparealm = NULL; char *iparealm = NULL;
char * conf_data = NULL; char * conf_data = NULL;
const char * princ = NULL; const char * princ = NULL;
const char * subject = NULL;
const char * hostdn = NULL; const char * hostdn = NULL;
struct utsname uinfo; struct utsname uinfo;
@ -548,7 +623,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
} }
if (bindpw) if (bindpw)
rval = join_ldap(ipaserver, hostname, &hostdn, bindpw, &princ, quiet); rval = join_ldap(ipaserver, hostname, &hostdn, bindpw, &princ, &subject, quiet);
else { else {
krberr = krb5_init_context(&krbctx); krberr = krb5_init_context(&krbctx);
if (krberr) { if (krberr) {
@ -569,7 +644,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
rval = 6; rval = 6;
goto cleanup; goto cleanup;
} }
rval = join_krb5(ipaserver, hostname, &hostdn, &princ, quiet); rval = join_krb5(ipaserver, hostname, &hostdn, &princ, &subject, quiet);
} }
if (rval) goto cleanup; if (rval) goto cleanup;
@ -629,7 +704,11 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
} }
cleanup: cleanup:
if (NULL != subject)
fprintf(stderr, "Certificate subject base is: %s\n", subject);
free((char *)princ); free((char *)princ);
free((char *)subject);
if (bindpw) if (bindpw)
ldap_memfree((void *)hostdn); ldap_memfree((void *)hostdn);
else else

View File

@ -113,6 +113,9 @@ class join(Command):
attrs_list = api.Command['host_add'](hostname)['result'] attrs_list = api.Command['host_add'](hostname)['result']
dn = attrs_list['dn'] dn = attrs_list['dn']
config = api.Command['config_show']()['result']
attrs_list['ipacertificatesubjectbase'] = config['ipacertificatesubjectbase']
return (dn, attrs_list) return (dn, attrs_list)
api.register(join) api.register(join)

View File

@ -295,7 +295,8 @@ class xmlserver(WSGIExecutioner):
self.info('response: %s: %s', error.__class__.__name__, str(error)) self.info('response: %s: %s', error.__class__.__name__, str(error))
response = Fault(error.errno, error.strerror) response = Fault(error.errno, error.strerror)
else: else:
self.info('response: entries returned %d', result.get('count', 1)) if isinstance(result, dict):
self.info('response: entries returned %d', result.get('count', 1))
response = (result,) response = (result,)
return xml_dumps(response, methodresponse=True) return xml_dumps(response, methodresponse=True)