diff options
-rw-r--r-- | source4/ldap_server/ldap_rootdse.c | 235 | ||||
-rw-r--r-- | source4/ldap_server/rootdse.ldif | 62 | ||||
-rwxr-xr-x | source4/script/rootdse.pl | 151 |
3 files changed, 407 insertions, 41 deletions
diff --git a/source4/ldap_server/ldap_rootdse.c b/source4/ldap_server/ldap_rootdse.c index 665bcde9af..b936c92da3 100644 --- a/source4/ldap_server/ldap_rootdse.c +++ b/source4/ldap_server/ldap_rootdse.c @@ -20,52 +20,121 @@ #include "includes.h" -#define ATTR_BLOB_CONST(val) data_blob_talloc(attrs, val, sizeof(val)-1) -#define ATTR_SINGLE_NOVAL(attr, blob, num, nam) do { \ - attr.name = talloc_strdup(attrs, nam);\ - if (!attr.name) {\ +#define ATTR_BLOB_CONST(val) data_blob_talloc(mem_ctx, val, sizeof(val)-1) +#define ATTR_SINGLE_NOVAL(ctx, attr, blob, num, nam) do { \ + attr->name = talloc_strdup(ctx, nam);\ + if (!attr->name) {\ + return NT_STATUS_NO_MEMORY;\ + }\ + attr->num_values = num; \ + attr->values = blob;\ +} while(0) +#define ALLOC_CHECK(ptr) do {\ + if (!(ptr)) {\ return NT_STATUS_NO_MEMORY;\ }\ - attr.num_values = num; \ - attr.values = blob;\ } while(0) -static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, - struct ldap_SearchRequest *r) + +struct rootdse_db_context { + struct ldb_context *ldb; + struct rootdse_db_context **static_ptr; +}; + +/* + this is used to catch debug messages from ldb +*/ +void rootdse_db_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) _PRINTF_ATTRIBUTE(3,0) { - struct ldap_SearchResEntry *ent; - struct ldap_Result *done; - int code = 0; - struct ldapsrv_reply *ent_r, *done_r; - int num_attrs = 3; - struct ldap_attribute *attrs; + char *s = NULL; + if (DEBUGLEVEL < 4 && level > LDB_DEBUG_WARNING) { + return; + } + vasprintf(&s, fmt, ap); + if (!s) return; + DEBUG(level, ("rootdse: %s\n", s)); + free(s); +} - DEBUG(10, ("Root DSE: %s\n", r->filter)); - if (r->scope != LDAP_SEARCH_SCOPE_BASE) { - code = 32; /* nosuchobject */ - goto no_base_scope; +/* destroy the last connection to the sam */ +static int rootdse_db_destructor(void *ctx) +{ + struct rootdse_db_context *rd_ctx = ctx; + ldb_close(rd_ctx->ldb); + *(rd_ctx->static_ptr) = NULL; + return 0; +} + +/* + connect to the SAM database + return an opaque context pointer on success, or NULL on failure + */ +void *rootdse_db_connect(TALLOC_CTX *mem_ctx) +{ + static struct rootdse_db_context *ctx; + char *db_path; + /* + the way that unix fcntl locking works forces us to have a + static ldb handle here rather than a much more sensible + approach of having the ldb handle as part of the + ldap base structures. Otherwise we would try to open + the ldb more than once, and tdb would rightly refuse the + second open due to the broken nature of unix locking. + */ + if (ctx != NULL) { + return talloc_reference(mem_ctx, ctx); } - attrs = talloc_array_p(call, struct ldap_attribute, num_attrs); - if (!attrs) { - return NT_STATUS_NO_MEMORY; + ctx = talloc_p(mem_ctx, struct rootdse_db_context); + if (ctx == NULL) { + errno = ENOMEM; + return NULL; + } + + ctx->static_ptr = &ctx; + + db_path = talloc_asprintf(ctx, "tdb://%s/rootdse.ldb", dyn_PRIVATE_DIR); + if (db_path == NULL) { + errno = ENOMEM; + return NULL; + } + + DEBUG(10, ("opening %s", db_path)); + ctx->ldb = ldb_connect(db_path, 0, NULL); + if (ctx->ldb == NULL) { + talloc_free(ctx); + return NULL; } + talloc_set_destructor(ctx, rootdse_db_destructor); + ldb_set_debug(ctx->ldb, rootdse_db_debug, NULL); + + return ctx; +} + + +static NTSTATUS fill_dynamic_values(void *mem_ctx, struct ldap_attribute *attrs) +{ /* * currentTime * 20040918090350.0Z */ + + DEBUG(10, ("fill_dynamic_values for %s\n", attrs[0].name)); + + if (strcasecmp(attrs->name, "currentTime") == 0) { int num_currentTime = 1; - DATA_BLOB *currentTime = talloc_array_p(attrs, DATA_BLOB, num_currentTime); - char *str = ldap_timestring(call, time(NULL)); + DATA_BLOB *currentTime = talloc_array_p(mem_ctx, DATA_BLOB, num_currentTime); + char *str = ldap_timestring(mem_ctx, time(NULL)); if (!str) { return NT_STATUS_NO_MEMORY; } currentTime[0].data = str; currentTime[0].length = strlen(str); - ATTR_SINGLE_NOVAL(attrs[0], currentTime, num_currentTime, "currentTime"); + ATTR_SINGLE_NOVAL(mem_ctx, attrs, currentTime, num_currentTime, "currentTime"); + return NT_STATUS_OK; } /* @@ -137,11 +206,13 @@ static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldaps * 3 * 2 */ + if (strcasecmp(attrs->name, "supportedLDAPVersion") == 0) { int num_supportedLDAPVersion = 1; - DATA_BLOB *supportedLDAPVersion = talloc_array_p(attrs, DATA_BLOB, num_supportedLDAPVersion); + DATA_BLOB *supportedLDAPVersion = talloc_array_p(mem_ctx, DATA_BLOB, num_supportedLDAPVersion); supportedLDAPVersion[0] = ATTR_BLOB_CONST("3"); - ATTR_SINGLE_NOVAL(attrs[1], supportedLDAPVersion, num_supportedLDAPVersion, "supportedLDAPVersion"); + ATTR_SINGLE_NOVAL(mem_ctx, attrs, supportedLDAPVersion, num_supportedLDAPVersion, "supportedLDAPVersion"); + return NT_STATUS_OK; } /* @@ -177,12 +248,6 @@ static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldaps * dnsHostName * netbiosname.dom.tld */ - { - int num_dnsHostName = 1; - DATA_BLOB *dnsHostName = talloc_array_p(attrs, DATA_BLOB, num_dnsHostName); - dnsHostName[0] = data_blob_talloc(attrs, lp_netbios_name(),strlen(lp_netbios_name())); - ATTR_SINGLE_NOVAL(attrs[2], dnsHostName, num_dnsHostName, "dnsHostName"); - } /* * ldapServiceName @@ -226,18 +291,89 @@ static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldaps * 2 */ +def: + { + DATA_BLOB *x = talloc_array_p(mem_ctx, DATA_BLOB, 1); + x[0] = ATTR_BLOB_CONST("0"); + ATTR_SINGLE_NOVAL(mem_ctx, attrs, x, 1, attrs->name); + } + return NT_STATUS_OK; +} - ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry); - if (!ent_r) { - return NT_STATUS_NO_MEMORY; +static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, + struct ldap_SearchRequest *r) +{ + NTSTATUS status; + void *local_ctx; + struct ldap_SearchResEntry *ent; + struct ldap_Result *done; + struct ldb_message **res; + int result = 0; + struct ldapsrv_reply *ent_r, *done_r; + struct rootdse_db_context *rootdsedb; + const char *errstr = NULL; + int count, i, j, y; + + if (r->scope != LDAP_SEARCH_SCOPE_BASE) { + count = -1; + goto no_base_scope; + } + + local_ctx = talloc_named(call, 0, "rootdse_Search local memory context"); + ALLOC_CHECK(local_ctx); + + rootdsedb = rootdse_db_connect(local_ctx); + ALLOC_CHECK(rootdsedb); + + ldb_set_alloc(rootdsedb->ldb, talloc_realloc_fn, rootdsedb); + count = ldb_search(rootdsedb->ldb, "", 0, "dn=rootDSE", NULL, &res); + + for (i = 0; i < count; i++) { + ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry); + if (!ent_r) { + return NT_STATUS_NO_MEMORY; + } + + ent = &ent_r->msg.r.SearchResultEntry; + ent->dn = ""; + ent->num_attributes = 0; + ent->attributes = NULL; + if (res[i]->num_elements == 0) { + goto queue_reply; + } + ent->num_attributes = res[i]->num_elements; + ent->attributes = talloc_array_p(ent_r, struct ldap_attribute, ent->num_attributes); + ALLOC_CHECK(ent->attributes); + for (j=0; j < ent->num_attributes; j++) { + ent->attributes[j].name = talloc_steal(ent->attributes, res[i]->elements[j].name); + ent->attributes[j].num_values = 0; + ent->attributes[j].values = NULL; + ent->attributes[j].num_values = res[i]->elements[j].num_values; + if (ent->attributes[j].num_values == 1 && + strncmp(res[i]->elements[j].values[0].data, "_DYNAMIC_", 9) == 0) { + fill_dynamic_values(ent->attributes, &(ent->attributes[j])); + if (ent->attributes[j].values[0].data == NULL) { + DEBUG (10, ("ARRGHH!\n")); + } + } else { + ent->attributes[j].values = talloc_array_p(ent->attributes, + DATA_BLOB, ent->attributes[j].num_values); + ALLOC_CHECK(ent->attributes[j].values); + for (y=0; y < ent->attributes[j].num_values; y++) { + ent->attributes[j].values[y].length = res[i]->elements[j].values[y].length; + ent->attributes[j].values[y].data = talloc_steal(ent->attributes[j].values, + res[i]->elements[j].values[y].data); + } + } + } +queue_reply: + status = ldapsrv_queue_reply(call, ent_r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } - ent = &ent_r->msg.r.SearchResultEntry; - ent->dn = ""; - ent->num_attributes = num_attrs; - ent->attributes = attrs; - ldapsrv_queue_reply(call, ent_r); no_base_scope: @@ -246,12 +382,29 @@ no_base_scope: return NT_STATUS_NO_MEMORY; } + if (count > 0) { + DEBUG(10,("rootdse_Search: results: [%d]\n",count)); + result = 0; + errstr = NULL; + } else if (count == 0) { + DEBUG(10,("rootdse_Search: no results\n")); + result = 32; /* nosuchobject */ + errstr = talloc_strdup(done_r, ldb_errstring(rootdsedb->ldb)); + } else if (count == -1) { + DEBUG(10,("rootdse_Search: error\n")); + result = 1; + errstr = talloc_strdup(done_r, ldb_errstring(rootdsedb->ldb)); + } + + done = &done_r->msg.r.SearchResultDone; - done->resultcode = code; + done->resultcode = result; done->dn = NULL; done->errormessage = NULL; done->referral = NULL; + talloc_free(local_ctx); + return ldapsrv_queue_reply(call, done_r); } diff --git a/source4/ldap_server/rootdse.ldif b/source4/ldap_server/rootdse.ldif new file mode 100644 index 0000000000..f129492732 --- /dev/null +++ b/source4/ldap_server/rootdse.ldif @@ -0,0 +1,62 @@ +dn: rootDSE +currentTime: _DYNAMIC_ +subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,${BASEDN} +dsServiceName: CN=NTDS Settings,CN=${NETBIOSNAME},CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,${BASEDN} +namingContexts: ${BASEDN} +namingContexts: CN=Configuration,${BASEDN} +namingContexts: CN=Schema,CN=Configuration,${BASEDN} +namingContexts: DC=DomainDnsZones,${BASEDN} +namingContexts: DC=ForestDnsZones,${BASEDN} +defaultNamingContext: ${BASEDN} +rootDomainNamingContext: ${BASEDN} +configurationNamingContext: CN=Configuration,${BASEDN} +schemaNamingContext: CN=Schema,CN=Configuration,${BASEDN} +supportedControl: 1.2.840.113556.1.4.319 +supportedControl: 1.2.840.113556.1.4.801 +supportedControl: 1.2.840.113556.1.4.473 +supportedControl: 1.2.840.113556.1.4.528 +supportedControl: 1.2.840.113556.1.4.417 +supportedControl: 1.2.840.113556.1.4.619 +supportedControl: 1.2.840.113556.1.4.841 +supportedControl: 1.2.840.113556.1.4.529 +supportedControl: 1.2.840.113556.1.4.805 +supportedControl: 1.2.840.113556.1.4.521 +supportedControl: 1.2.840.113556.1.4.970 +supportedControl: 1.2.840.113556.1.4.1338 +supportedControl: 1.2.840.113556.1.4.474 +supportedControl: 1.2.840.113556.1.4.1339 +supportedControl: 1.2.840.113556.1.4.1340 +supportedControl: 1.2.840.113556.1.4.1413 +supportedControl: 2.16.840.1.113730.3.4.9 +supportedControl: 2.16.840.1.113730.3.4.10 +supportedControl: 1.2.840.113556.1.4.1504 +supportedControl: 1.2.840.113556.1.4.1852 +supportedControl: 1.2.840.113556.1.4.802 +supportedLDAPVersion: 3 +supportedLDAPPolicies: MaxPoolThreads +supportedLDAPPolicies: MaxDatagramRecv +supportedLDAPPolicies: MaxReceiveBuffer +supportedLDAPPolicies: InitRecvTimeout +supportedLDAPPolicies: MaxConnections +supportedLDAPPolicies: MaxConnIdleTime +supportedLDAPPolicies: MaxPageSize +supportedLDAPPolicies: MaxQueryDuration +supportedLDAPPolicies: MaxTempTableSize +supportedLDAPPolicies: MaxResultSetSize +supportedLDAPPolicies: MaxNotificationPerConn +supportedLDAPPolicies: MaxValRange +highestCommittedUSN: _DYNAMIC_ +supportedSASLMechanisms: GSSAPI +supportedSASLMechanisms: GSS-SPNEGO +supportedSASLMechanisms: EXTERNAL +supportedSASLMechanisms: DIGEST-MD5 +dnsHostName: ${NETBIOSNAME}.${DNSDOMAIN} +ldapServiceName: ${DNSDOMAIN}:${NETBIOSNAME}$@${DNSDOMAIN} +serverName: CN=${NETBIOSNAME},CN=Servers,CN=Default-First-Site,CN=Sites,CN=Configuration,${BASEDN} +supportedCapabilities: 1.2.840.113556.1.4.800 +supportedCapabilities: 1.2.840.113556.1.4.1670 +supportedCapabilities: 1.2.840.113556.1.4.1791 +isSynchronized: _DYNAMIC_ +domainFunctionality: 0 +forestFunctionality: 0 +domainControllerFunctionality: 2 diff --git a/source4/script/rootdse.pl b/source4/script/rootdse.pl new file mode 100755 index 0000000000..5e620a0991 --- /dev/null +++ b/source4/script/rootdse.pl @@ -0,0 +1,151 @@ +#!/usr/bin/perl -w + +use strict; +use Getopt::Long; + +my $opt_hostname = `hostname`; +chomp $opt_hostname; +my $opt_realm; +my $opt_domain; +my $opt_netbiosname; +my $dnsdomain; +my $dnsname; +my $basedn; + +sub ldaptime() +{ + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime(time); + return sprintf "%04u%02u%02u%02u%02u%02u.0Z", + $year+1900, $mon+1, $mday, $hour, $min, $sec; +} + +####################### +# substitute a single variable +sub substitute($) +{ + my $var = shift; + + if ($var eq "BASEDN") { + return $basedn; + } + + if ($var eq "NETBIOSNAME") { + return $opt_netbiosname; + } + + if ($var eq "DNSDOMAIN") { + return $dnsdomain; + } + + die "ERROR: Uknown substitution variable $var\n"; +} + +##################################################################### +# write a string into a file +sub FileSave($$) +{ + my($filename) = shift; + my($v) = shift; + local(*FILE); + open(FILE, ">$filename") || die "can't open $filename"; + print FILE $v; + close(FILE); +} + +##################################################################### +# read a file into a string +sub FileLoad($) +{ + my($filename) = shift; + local(*INPUTFILE); + open(INPUTFILE, $filename) || return undef; + my($saved_delim) = $/; + undef $/; + my($data) = <INPUTFILE>; + close(INPUTFILE); + $/ = $saved_delim; + return $data; +} + +############################################ +# show some help +sub ShowHelp() +{ + print " +Samba4 provisioning + +rootdse.pl [options] + --realm REALM set realm + --domain DOMAIN set domain + --hostname HOSTNAME set hostname + --netbiosname NETBIOSNAME choose admin password (otherwise random) + +You must provide at least a realm and domain + +"; + exit(1); +} + +my $opt_help; + +GetOptions( + 'help|h|?' => \$opt_help, + 'realm=s' => \$opt_realm, + 'domain=s' => \$opt_domain, + 'hostname=s' => \$opt_hostname, + 'netbiosname=s' => \$opt_netbiosname, + ); + +if ($opt_help || + !$opt_realm || + !$opt_domain || + !$opt_hostname || + !$opt_netbiosname) { + ShowHelp(); +} + +$opt_realm=uc($opt_realm); +$opt_domain=uc($opt_domain); +$opt_hostname=uc($opt_hostname); +$opt_netbiosname=uc($opt_netbiosname); + +print "Provisioning host '$opt_hostname' with netbios name '$opt_netbiosname' for domain '$opt_domain' in realm '$opt_realm'\n"; + +print "generating ldif ...\n"; + +$dnsdomain = lc($opt_realm); +$dnsname = lc($opt_hostname).".".$dnsdomain; +$basedn = "DC=" . join(",DC=", split(/\./, $opt_realm)); + +my $data = FileLoad("rootdse.ldif") || die "Unable to load rootdse.ldif\n"; + +my $res = ""; + +print "applying substitutions ...\n"; + +while ($data =~ /(.*?)\$\{(\w*)\}(.*)/s) { + my $sub = substitute($2); + $res .= "$1$sub"; + $data = $3; +} +$res .= $data; + +print "saving ldif to newrootdse.ldif ...\n"; + +FileSave("newrootdse.ldif", $res); + +unlink("newrootdse.ldb"); + +print "creating newrootdse.ldb ...\n"; + +# allow provisioning to be run from the source directory +$ENV{"PATH"} .= ":bin"; + +system("ldbadd -H newrootdse.ldb newroodse.ldif"); + +print "done + +Please move newrootdse.ldb to rootdse.ldb in the lib/private/ directory of your +Samba4 installation +"; + |