From f2fb6552c91fa530597e6deb776d90344bfe67bd Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Tue, 11 Oct 2011 17:30:33 -0400 Subject: 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 --- ipa-client/ipa-install/ipa-client-install | 4 +- ipa-client/ipa-join.c | 194 ++++++++++++++---------------- ipa-client/man/ipa-join.1 | 5 +- 3 files changed, 94 insertions(+), 109 deletions(-) diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index 1b9c48845..a1d3d6577 100755 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -31,7 +31,7 @@ try: from ipaclient import ipadiscovery import ipaclient.ipachangeconf 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 from ipapython import ipautil from ipapython import dnsclient @@ -942,7 +942,7 @@ def install(options, env, fstore, statestore): print "Test kerberos configuration failed" return CLIENT_INSTALL_ERROR 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: join_args.append("-d") if options.hostname: diff --git a/ipa-client/ipa-join.c b/ipa-client/ipa-join.c index aac80976d..6a8523135 100644 --- a/ipa-client/ipa-join.c +++ b/ipa-client/ipa-join.c @@ -260,9 +260,11 @@ get_root_dn(const char *ipaserver, char **ldap_base) { LDAP *ld = NULL; char *root_attrs[] = {"namingContexts", NULL}; + char *info_attrs[] = {"info", NULL}; LDAPMessage *entry, *res = NULL; struct berval **ncvals; - int ret, rval = 0; + struct berval **infovals; + int i, ret, rval = 0; ld = connect_ldap(ipaserver, NULL, NULL); if (!ld) { @@ -281,7 +283,9 @@ get_root_dn(const char *ipaserver, char **ldap_base) 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); ncvals = ldap_get_values_len(ld, entry, root_attrs[0]); if (!ncvals) { @@ -289,11 +293,38 @@ get_root_dn(const char *ipaserver, char **ldap_base) rval = 14; 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); + 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: if (res) ldap_msgfree(res); if (ld != NULL) { @@ -303,25 +334,31 @@ done: 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 -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 base[LINE_MAX]; + char *base = NULL; LDAPMessage *entry, *res = NULL; struct berval **ncvals; int ret, rval = 0; - ld = connect_ldap(ipaserver, NULL, NULL); - if (!ld) { - rval = 14; + ret = asprintf(&base, "cn=ipaconfig,cn=etc,%s", ldap_base); + if (ret == -1) + { + if (!quiet) + fprintf(stderr, _("Out of memory!\n")); + rval = 3; 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); @@ -347,10 +384,8 @@ get_subject(const char *ipaserver, char *ldap_base, const char **subject) ldap_value_free_len(ncvals); done: + free(base); if (res) ldap_msgfree(res); - if (ld != NULL) { - ldap_unbind_ext(ld, NULL, NULL); - } return rval; } @@ -374,61 +409,41 @@ done: * the state of the entry. */ 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; char *filter = NULL; int rval = 0; - char *oidresult; + char *oidresult = NULL; struct berval valrequest; struct berval *valresult = NULL; int rc, ret; - LDAPMessage *result, *e; char *ldap_base = NULL; char *search_base = NULL; - char * attrs[] = {"krbPrincipalName", NULL}; - struct berval **ncvals; - int has_principal = 0; *binddn = NULL; *princ = NULL; *subject = NULL; - 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) { - 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 (!quiet) - fprintf(stderr, _("Out of memory!\n")); - rval = 3; - goto done; + if (NULL != basedn) { + ldap_base = strdup(basedn); + if (!ldap_base) { + if (!quiet) + fprintf(stderr, _("Out of memory!\n")); + 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; + } } - ret = asprintf(&search_base, "cn=computers,cn=accounts,%s", ldap_base); + ret = asprintf(binddn, "fqdn=%s,cn=computers,cn=accounts,%s", hostname, ldap_base); if (ret == -1) { if (!quiet) @@ -436,52 +451,6 @@ join_ldap(const char *ipaserver, char *hostname, const char ** binddn, const cha 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); if (!ld) { if (!quiet) @@ -490,6 +459,14 @@ join_ldap(const char *ipaserver, char *hostname, const char ** binddn, const cha 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_len = strlen(hostname); @@ -525,11 +502,12 @@ ldap_done: done: if (valresult) ber_bvfree(valresult); + if (oidresult) free(oidresult); return rval; } 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_value * argArrayP = 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 */ 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_array_read_item(&env, resultP, 1, &structP); @@ -883,7 +861,7 @@ cleanup: 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; pid_t childpid = 0; @@ -893,7 +871,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k char * host = NULL; const char * princ = NULL; const char * subject = NULL; - const char * hostdn = NULL; + char * hostdn = NULL; struct utsname uinfo; krb5_context krbctx = NULL; @@ -927,7 +905,7 @@ join(const char *server, const char *hostname, const char *bindpw, const char *k } if (bindpw) - rval = join_ldap(ipaserver, host, &hostdn, bindpw, &princ, &subject, quiet); + rval = join_ldap(ipaserver, host, &hostdn, bindpw, basedn, &princ, &subject, quiet); else { krberr = krb5_init_context(&krbctx); if (krberr) { @@ -1017,6 +995,7 @@ cleanup: free((char *)princ); free((char *)subject); + free(host); if (bindpw) ldap_memfree((void *)hostdn); @@ -1044,6 +1023,7 @@ main(int argc, const char **argv) { static const char *server = NULL; static const char *keytab = NULL; static const char *bindpw = NULL; + static const char *basedn = NULL; int quiet = 0; int unenroll = 0; struct poptOption options[] = { @@ -1061,6 +1041,8 @@ main(int argc, const char **argv) { _("Specifies where to store keytab information."), _("filename") }, { "bindpw", 'w', POPT_ARG_STRING, &bindpw, 0, _("LDAP password (if not using Kerberos)"), _("password") }, + { "basedn", 'b', POPT_ARG_STRING, &basedn, 0, + _("LDAP basedn"), _("basedn") }, POPT_AUTOHELP POPT_TABLEEND }; @@ -1093,7 +1075,7 @@ main(int argc, const char **argv) { } else { ret = check_perms(keytab); if (ret == 0) - ret = join(server, hostname, bindpw, keytab, quiet); + ret = join(server, hostname, bindpw, basedn, keytab, quiet); } exit(ret); diff --git a/ipa-client/man/ipa-join.1 b/ipa-client/man/ipa-join.1 index 60facdfa9..49887c7a0 100644 --- a/ipa-client/man/ipa-join.1 +++ b/ipa-client/man/ipa-join.1 @@ -20,7 +20,7 @@ .SH "NAME" ipa\-join \- Join a machine to an IPA realm and get a keytab for the host service principal .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" 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 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 +\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 Unenroll this host from the IPA server. No keytab entry is removed in the process (see -- cgit