Make ipa-join work against an LDAP server that disallows anon binds

We determine the realm in the client installer so we can deduce
the base dn, pass that into ipa-join so we don't have to hunt for
it.

Re-order the bind so when doing an OTP enrollment so we can use the host
entry to authenticate before we retrieve the subject base, then initiate
the enrollment.

If ipa-join is called without a basedn it will still attempt to
determine it, but it will fail if anonymous binds are not allowed.

https://fedorahosted.org/freeipa/ticket/1935
This commit is contained in:
Rob Crittenden 2011-10-11 17:30:33 -04:00
parent 498311d2ef
commit f2fb6552c9
3 changed files with 94 additions and 109 deletions

View File

@ -31,7 +31,7 @@ try:
from ipaclient import ipadiscovery from ipaclient import ipadiscovery
import ipaclient.ipachangeconf import ipaclient.ipachangeconf
import ipaclient.ntpconf import ipaclient.ntpconf
from ipapython.ipautil import run, user_input, CalledProcessError, file_exists from ipapython.ipautil import run, user_input, CalledProcessError, file_exists, realm_to_suffix
import ipapython.services as ipaservices import ipapython.services as ipaservices
from ipapython import ipautil from ipapython import ipautil
from ipapython import dnsclient from ipapython import dnsclient
@ -942,7 +942,7 @@ def install(options, env, fstore, statestore):
print "Test kerberos configuration failed" print "Test kerberos configuration failed"
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
env['KRB5_CONFIG'] = krb_name env['KRB5_CONFIG'] = krb_name
join_args = ["/usr/sbin/ipa-join", "-s", cli_server] join_args = ["/usr/sbin/ipa-join", "-s", cli_server, "-b", realm_to_suffix(cli_realm)]
if options.debug: if options.debug:
join_args.append("-d") join_args.append("-d")
if options.hostname: if options.hostname:

View File

@ -260,9 +260,11 @@ get_root_dn(const char *ipaserver, char **ldap_base)
{ {
LDAP *ld = NULL; LDAP *ld = NULL;
char *root_attrs[] = {"namingContexts", NULL}; char *root_attrs[] = {"namingContexts", NULL};
char *info_attrs[] = {"info", NULL};
LDAPMessage *entry, *res = NULL; LDAPMessage *entry, *res = NULL;
struct berval **ncvals; struct berval **ncvals;
int ret, rval = 0; struct berval **infovals;
int i, ret, rval = 0;
ld = connect_ldap(ipaserver, NULL, NULL); ld = connect_ldap(ipaserver, NULL, NULL);
if (!ld) { if (!ld) {
@ -281,7 +283,9 @@ get_root_dn(const char *ipaserver, char **ldap_base)
goto done; goto done;
} }
/* for now just use the first result we get */ *ldap_base = NULL;
/* loop through to find the IPA context */
entry = ldap_first_entry(ld, res); entry = ldap_first_entry(ld, res);
ncvals = ldap_get_values_len(ld, entry, root_attrs[0]); ncvals = ldap_get_values_len(ld, entry, root_attrs[0]);
if (!ncvals) { if (!ncvals) {
@ -289,11 +293,38 @@ get_root_dn(const char *ipaserver, char **ldap_base)
rval = 14; rval = 14;
goto done; goto done;
} }
for (i = 0; !*ldap_base && ncvals[i]; i++) {
ret = ldap_search_ext_s(ld, ncvals[i]->bv_val,
LDAP_SCOPE_BASE, "(info=IPA*)", info_attrs,
0, NULL, NULL, NULL, 0, &res);
*ldap_base = strdup(ncvals[0]->bv_val); if (ret != LDAP_SUCCESS) {
break;
}
entry = ldap_first_entry(ld, res);
infovals = ldap_get_values_len(ld, entry, info_attrs[0]);
if (!strcmp(infovals[0]->bv_val, "IPA V2.0"))
*ldap_base = strdup(ncvals[i]->bv_val);
ldap_msgfree(res);
res = NULL;
}
ldap_value_free_len(ncvals); ldap_value_free_len(ncvals);
if (ret != LDAP_SUCCESS) {
fprintf(stderr, _("Search for IPA namingContext failed with error %d\n"), ret);
rval = 14;
goto done;
}
if (!*ldap_base) {
fprintf(stderr, _("IPA namingContext not found\n"));
rval = 14;
goto done;
}
done: done:
if (res) ldap_msgfree(res); if (res) ldap_msgfree(res);
if (ld != NULL) { if (ld != NULL) {
@ -303,25 +334,31 @@ done:
return rval; return rval;
} }
/*
* Get the certificate subject base from the IPA configuration.
*
* Not considered a show-stopper if this fails for some reason.
*
* The caller is responsible for binding/unbinding to LDAP.
*/
static int static int
get_subject(const char *ipaserver, char *ldap_base, const char **subject) get_subject(LDAP *ld, char *ldap_base, const char **subject, int quiet)
{ {
LDAP *ld = NULL;
char *attrs[] = {"ipaCertificateSubjectBase", NULL}; char *attrs[] = {"ipaCertificateSubjectBase", NULL};
char base[LINE_MAX]; char *base = NULL;
LDAPMessage *entry, *res = NULL; LDAPMessage *entry, *res = NULL;
struct berval **ncvals; struct berval **ncvals;
int ret, rval = 0; int ret, rval = 0;
ld = connect_ldap(ipaserver, NULL, NULL); ret = asprintf(&base, "cn=ipaconfig,cn=etc,%s", ldap_base);
if (!ld) { if (ret == -1)
rval = 14; {
if (!quiet)
fprintf(stderr, _("Out of memory!\n"));
rval = 3;
goto done; goto done;
} }
strcpy(base, "cn=ipaconfig,cn=etc,");
strcat(base, ldap_base);
ret = ldap_search_ext_s(ld, base, LDAP_SCOPE_BASE, ret = ldap_search_ext_s(ld, base, LDAP_SCOPE_BASE,
"objectclass=*", attrs, 0, "objectclass=*", attrs, 0,
NULL, NULL, NULL, 0, &res); NULL, NULL, NULL, 0, &res);
@ -347,10 +384,8 @@ get_subject(const char *ipaserver, char *ldap_base, const char **subject)
ldap_value_free_len(ncvals); ldap_value_free_len(ncvals);
done: done:
free(base);
if (res) ldap_msgfree(res); if (res) ldap_msgfree(res);
if (ld != NULL) {
ldap_unbind_ext(ld, NULL, NULL);
}
return rval; return rval;
} }
@ -374,52 +409,41 @@ done:
* the state of the entry. * the state of the entry.
*/ */
static int static int
join_ldap(const char *ipaserver, char *hostname, const char ** binddn, const char *bindpw, const char **princ, const char **subject, int quiet) join_ldap(const char *ipaserver, char *hostname, char ** binddn, const char *bindpw, const char *basedn, const char **princ, const char **subject, int quiet)
{ {
LDAP *ld; LDAP *ld;
char *filter = NULL; char *filter = NULL;
int rval = 0; int rval = 0;
char *oidresult; char *oidresult = NULL;
struct berval valrequest; struct berval valrequest;
struct berval *valresult = NULL; struct berval *valresult = NULL;
int rc, ret; int rc, ret;
LDAPMessage *result, *e;
char *ldap_base = NULL; char *ldap_base = NULL;
char *search_base = NULL; char *search_base = NULL;
char * attrs[] = {"krbPrincipalName", NULL};
struct berval **ncvals;
int has_principal = 0;
*binddn = NULL; *binddn = NULL;
*princ = NULL; *princ = NULL;
*subject = NULL; *subject = NULL;
if (get_root_dn(ipaserver, &ldap_base) != 0) { if (NULL != basedn) {
if (!quiet) ldap_base = strdup(basedn);
fprintf(stderr, _("Unable to determine root DN of %s\n"), if (!ldap_base) {
ipaserver); if (!quiet)
rval = 14; fprintf(stderr, _("Out of memory!\n"));
goto done; rval = 3;
goto done;
}
} else {
if (get_root_dn(ipaserver, &ldap_base) != 0) {
if (!quiet)
fprintf(stderr, _("Unable to determine root DN of %s\n"),
ipaserver);
rval = 14;
goto done;
}
} }
if (get_subject(ipaserver, ldap_base, subject) != 0) { ret = asprintf(binddn, "fqdn=%s,cn=computers,cn=accounts,%s", hostname, ldap_base);
if (!quiet)
fprintf(stderr,
_("Unable to determine certificate subject of %s\n"),
ipaserver);
/* Not a critical failure */
}
ld = connect_ldap(ipaserver, NULL, NULL);
if (!ld) {
if (!quiet)
fprintf(stderr, _("Unable to make an LDAP connection to %s\n"),
ipaserver);
rval = 14;
goto done;
}
/* Search for the entry. */
ret = asprintf(&filter, "(fqdn=%s)", hostname);
if (ret == -1) if (ret == -1)
{ {
if (!quiet) if (!quiet)
@ -427,61 +451,6 @@ join_ldap(const char *ipaserver, char *hostname, const char ** binddn, const cha
rval = 3; rval = 3;
goto done; goto done;
} }
ret = asprintf(&search_base, "cn=computers,cn=accounts,%s", ldap_base);
if (ret == -1)
{
if (!quiet)
fprintf(stderr, _("Out of memory!\n"));
rval = 3;
goto done;
}
if (debug) {
fprintf(stderr, _("Searching with %s in %s\n"), filter, search_base);
}
if ((ret = ldap_search_ext_s(ld, ldap_base, LDAP_SCOPE_SUB,
filter, attrs, 0, NULL, NULL, LDAP_NO_LIMIT,
LDAP_NO_LIMIT, &result)) != LDAP_SUCCESS) {
if (!quiet)
fprintf(stderr, _("ldap_search_ext_s: %s\n"),
ldap_err2string(ret));
rval = 14;
goto ldap_done;
}
e = ldap_first_entry(ld, result);
if (!e) {
if (!quiet)
fprintf(stderr, _("Unable to find host '%s'\n"), hostname);
rval = 14;
goto ldap_done;
}
if ((*binddn = ldap_get_dn(ld, e)) == NULL) {
if (!quiet)
fprintf(stderr,
_("Unable to get binddn for host '%s'\n"), hostname);
rval = 14;
goto ldap_done;
}
ncvals = ldap_get_values_len(ld, e, attrs[0]);
if (ncvals != NULL) {
/* This host is probably already registered. The krbprincipalname
* is not set on password protected entries, but lets try to bind
* anyway.
*/
has_principal = 1;
if (debug)
fprintf(stderr,
_("Host already has principal, trying bind anyway\n"));
}
ldap_value_free_len(ncvals);
ldap_msgfree(result);
if (ld != NULL) {
ldap_unbind_ext(ld, NULL, NULL);
}
/* Now rebind as the host */
ld = connect_ldap(ipaserver, *binddn, bindpw); ld = connect_ldap(ipaserver, *binddn, bindpw);
if (!ld) { if (!ld) {
if (!quiet) if (!quiet)
@ -490,6 +459,14 @@ join_ldap(const char *ipaserver, char *hostname, const char ** binddn, const cha
goto done; goto done;
} }
if (get_subject(ld, ldap_base, subject, quiet) != 0) {
if (!quiet)
fprintf(stderr,
_("Unable to determine certificate subject of %s\n"),
ipaserver);
/* Not a critical failure */
}
valrequest.bv_val = (char *)hostname; valrequest.bv_val = (char *)hostname;
valrequest.bv_len = strlen(hostname); valrequest.bv_len = strlen(hostname);
@ -525,11 +502,12 @@ ldap_done:
done: done:
if (valresult) ber_bvfree(valresult); if (valresult) ber_bvfree(valresult);
if (oidresult) free(oidresult);
return rval; return rval;
} }
static int static int
join_krb5(const char *ipaserver, char *hostname, const char **hostdn, const char **princ, const char **subject, int quiet) { join_krb5(const char *ipaserver, char *hostname, char **hostdn, const char **princ, const char **subject, int quiet) {
xmlrpc_env env; xmlrpc_env env;
xmlrpc_value * argArrayP = NULL; xmlrpc_value * argArrayP = NULL;
xmlrpc_value * paramArrayP = NULL; xmlrpc_value * paramArrayP = NULL;
@ -607,7 +585,7 @@ join_krb5(const char *ipaserver, char *hostname, const char **hostdn, const char
* DN, the second a struct of attribute values * DN, the second a struct of attribute values
*/ */
xmlrpc_array_read_item(&env, resultP, 0, &hostdnP); xmlrpc_array_read_item(&env, resultP, 0, &hostdnP);
xmlrpc_read_string(&env, hostdnP, &*hostdn); xmlrpc_read_string(&env, hostdnP, (const char **)hostdn);
xmlrpc_DECREF(hostdnP); xmlrpc_DECREF(hostdnP);
xmlrpc_array_read_item(&env, resultP, 1, &structP); xmlrpc_array_read_item(&env, resultP, 1, &structP);
@ -883,7 +861,7 @@ cleanup:
static int static int
join(const char *server, const char *hostname, const char *bindpw, const char *keytab, int quiet) join(const char *server, const char *hostname, const char *bindpw, const char *basedn, const char *keytab, int quiet)
{ {
int rval = 0; int rval = 0;
pid_t childpid = 0; pid_t childpid = 0;
@ -893,7 +871,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
char * host = NULL; char * host = NULL;
const char * princ = NULL; const char * princ = NULL;
const char * subject = NULL; const char * subject = NULL;
const char * hostdn = NULL; char * hostdn = NULL;
struct utsname uinfo; struct utsname uinfo;
krb5_context krbctx = NULL; krb5_context krbctx = NULL;
@ -927,7 +905,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k
} }
if (bindpw) if (bindpw)
rval = join_ldap(ipaserver, host, &hostdn, bindpw, &princ, &subject, quiet); rval = join_ldap(ipaserver, host, &hostdn, bindpw, basedn, &princ, &subject, quiet);
else { else {
krberr = krb5_init_context(&krbctx); krberr = krb5_init_context(&krbctx);
if (krberr) { if (krberr) {
@ -1017,6 +995,7 @@ cleanup:
free((char *)princ); free((char *)princ);
free((char *)subject); free((char *)subject);
free(host);
if (bindpw) if (bindpw)
ldap_memfree((void *)hostdn); ldap_memfree((void *)hostdn);
@ -1044,6 +1023,7 @@ main(int argc, const char **argv) {
static const char *server = NULL; static const char *server = NULL;
static const char *keytab = NULL; static const char *keytab = NULL;
static const char *bindpw = NULL; static const char *bindpw = NULL;
static const char *basedn = NULL;
int quiet = 0; int quiet = 0;
int unenroll = 0; int unenroll = 0;
struct poptOption options[] = { struct poptOption options[] = {
@ -1061,6 +1041,8 @@ main(int argc, const char **argv) {
_("Specifies where to store keytab information."), _("filename") }, _("Specifies where to store keytab information."), _("filename") },
{ "bindpw", 'w', POPT_ARG_STRING, &bindpw, 0, { "bindpw", 'w', POPT_ARG_STRING, &bindpw, 0,
_("LDAP password (if not using Kerberos)"), _("password") }, _("LDAP password (if not using Kerberos)"), _("password") },
{ "basedn", 'b', POPT_ARG_STRING, &basedn, 0,
_("LDAP basedn"), _("basedn") },
POPT_AUTOHELP POPT_AUTOHELP
POPT_TABLEEND POPT_TABLEEND
}; };
@ -1093,7 +1075,7 @@ main(int argc, const char **argv) {
} else { } else {
ret = check_perms(keytab); ret = check_perms(keytab);
if (ret == 0) if (ret == 0)
ret = join(server, hostname, bindpw, keytab, quiet); ret = join(server, hostname, bindpw, basedn, keytab, quiet);
} }
exit(ret); exit(ret);

View File

@ -20,7 +20,7 @@
.SH "NAME" .SH "NAME"
ipa\-join \- Join a machine to an IPA realm and get a keytab for the host service principal ipa\-join \- Join a machine to an IPA realm and get a keytab for the host service principal
.SH "SYNOPSIS" .SH "SYNOPSIS"
ipa\-join [\fB\-d\fR|\fB\-\-debug\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-u\fR|\fB\-\-unenroll\fR] [\fB\-h\fR|\fB\-\-hostname\fR hostname] [\fB\-s\fR|\fB\-\-server\fR hostame] [\fB\-k\fR|\fB\-\-keytab\fR filename] [\fB\-w\fR|\fB\-\-bindpw\fR password] [\fB\-?\fR|\fB\-\-help\fR] [\fB\-\-usage\fR] ipa\-join [\fB\-d\fR|\fB\-\-debug\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-u\fR|\fB\-\-unenroll\fR] [\fB\-h\fR|\fB\-\-hostname\fR hostname] [\fB\-s\fR|\fB\-\-server\fR hostame] [\fB\-k\fR|\fB\-\-keytab\fR filename] [\fB\-w\fR|\fB\-\-bindpw\fR password] [\fB-b\fR|\-\-\fBbasedn basedn\fR] [\fB\-?\fR|\fB\-\-help\fR] [\fB\-\-usage\fR]
.SH "DESCRIPTION" .SH "DESCRIPTION"
Joins a host to an IPA realm and retrieves a kerberos \fIkeytab\fR for the host service principal, or unenrolls an enrolled host from an IPA server. Joins a host to an IPA realm and retrieves a kerberos \fIkeytab\fR for the host service principal, or unenrolls an enrolled host from an IPA server.
@ -61,6 +61,9 @@ The keytab file where to append the new key (will be created if it does not exis
\fB\-w,\-\-bindpw password\fR \fB\-w,\-\-bindpw password\fR
The password to use if not using Kerberos to authenticate. Use a password of this particular host (one time password created on IPA server) The password to use if not using Kerberos to authenticate. Use a password of this particular host (one time password created on IPA server)
.TP .TP
\fB\-b,\-\-basedn basedn\fR
The basedn of the IPA server (of the form dc=example,dc=com). This is only needed when not using Kerberos to authenticate and anonymous binds are disallowed in the IPA LDAP server.
.TP
\fB\-u,\-\-unenroll\fR \fB\-u,\-\-unenroll\fR
Unenroll this host from the IPA server. No keytab entry is removed in the process Unenroll this host from the IPA server. No keytab entry is removed in the process
(see (see