From bceca723044e9cf5d835e8d732be3ab57906505e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 19 Dec 2005 07:07:11 +0000 Subject: r12361: Add a new function: ldb_binary_encode_string() This is for use on user-supplied arguments to printf style format strings which will become ldb filters. I have used it on LSA, SAMR and the auth/ code so far. Also add comments to cracknames code. Andrew Bartlett (This used to be commit 8308cf6e0472790c1c9d521d19322557907f4418) --- source4/auth/auth_sam.c | 5 +-- source4/dsdb/samdb/cracknames.c | 61 ++++++++++++++++++++++++++++------- source4/lib/ldb/common/ldb_parse.c | 13 ++++++++ source4/lib/ldb/include/ldb.h | 2 +- source4/rpc_server/lsa/dcesrv_lsa.c | 16 +++++---- source4/rpc_server/samr/dcesrv_samr.c | 12 ++++--- 6 files changed, 82 insertions(+), 27 deletions(-) diff --git a/source4/auth/auth_sam.c b/source4/auth/auth_sam.c index e65a5c70f6..95a7702822 100644 --- a/source4/auth/auth_sam.c +++ b/source4/auth/auth_sam.c @@ -280,10 +280,11 @@ static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context * const struct ldb_dn *domain_dn = NULL; if (domain_name) { + char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name); /* find the domain's DN */ ret_domain = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_domain_ref, domain_ref_attrs, "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", - domain_name, domain_name); + escaped_domain, escaped_domain); if (ret_domain == -1) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } @@ -306,7 +307,7 @@ static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context * /* pull the user attributes */ ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs, "(&(sAMAccountName=%s)(objectclass=user))", - account_name); + ldb_binary_encode_string(mem_ctx, account_name)); if (ret == -1) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c index c95ab047e3..2010005a6b 100644 --- a/source4/dsdb/samdb/cracknames.c +++ b/source4/dsdb/samdb/cracknames.c @@ -115,6 +115,11 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; } +/* When cracking a ServicePrincipalName, many services may be served + * by the host/ servicePrincipalName. The incoming query is for cifs/ + * but we translate it here, and search on host/. This is done after + * the cifs/ entry has been searched for, making this a fallback */ + static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, @@ -185,6 +190,8 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c return wret; } +/* Subcase of CrackNames, for the userPrincipalName */ + static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, @@ -214,7 +221,8 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal); domain_filter = talloc_asprintf(mem_ctx, "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", - *realm, *realm); + ldb_binary_encode_string(mem_ctx, *realm), + ldb_binary_encode_string(mem_ctx, *realm)); ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short); krb5_free_principal(smb_krb5_context->krb5_context, principal); @@ -225,7 +233,7 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, /* This may need to be extended for more userPrincipalName variations */ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", - unparsed_name_short); + ldb_binary_encode_string(mem_ctx, unparsed_name_short)); if (!result_filter || !domain_filter) { free(unparsed_name_short); return WERR_NOMEM; @@ -239,6 +247,8 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, return status; } +/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */ + WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) @@ -284,7 +294,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, domain_filter = talloc_asprintf(mem_ctx, "(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))", - str); + ldb_binary_encode_string(mem_ctx, str)); WERR_TALLOC_CHECK(domain_filter); break; @@ -311,11 +321,11 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, domain_filter = talloc_asprintf(mem_ctx, "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", - domain); + ldb_binary_encode_string(mem_ctx, domain)); WERR_TALLOC_CHECK(domain_filter); if (account) { result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)", - account); + ldb_binary_encode_string(mem_ctx, account)); WERR_TALLOC_CHECK(result_filter); } @@ -356,7 +366,8 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, domain_filter = NULL; result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))", - name, name); + ldb_binary_encode_string(mem_ctx, name), + ldb_binary_encode_string(mem_ctx, name)); WERR_TALLOC_CHECK(result_filter); break; } @@ -399,7 +410,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, krb5_free_principal(smb_krb5_context->krb5_context, principal); result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))", - unparsed_name); + ldb_binary_encode_string(mem_ctx, unparsed_name)); free(unparsed_name); WERR_TALLOC_CHECK(result_filter); @@ -408,6 +419,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { krb5_principal principal; char *unparsed_name_short; + char *service; ret = krb5_parse_name_norealm(smb_krb5_context->krb5_context, name, &principal); if (ret) { /* perhaps it's a principal with a realm, so return the right 'domain only' response */ @@ -437,13 +449,20 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, domain_filter = NULL; ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short); - krb5_free_principal(smb_krb5_context->krb5_context, principal); if (ret) { + krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } - - result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(servicePrincipalName=%s))", - unparsed_name_short); + service = principal->name.name_string.val[0]; + if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) { + result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))", + ldb_binary_encode_string(mem_ctx, unparsed_name_short), + ldb_binary_encode_string(mem_ctx, principal->name.name_string.val[1])); + } else { + result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))", + ldb_binary_encode_string(mem_ctx, unparsed_name_short)); + } + krb5_free_principal(smb_krb5_context->krb5_context, principal); free(unparsed_name_short); WERR_TALLOC_CHECK(result_filter); @@ -469,6 +488,10 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, info1); } +/* Subcase of CrackNames. It is possible to translate a LDAP-style DN + * (FQDN_1779) into a canoical name without actually searching the + * database */ + static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, uint32_t format_offered, uint32_t format_desired, const struct ldb_dn *name_dn, const char *name, @@ -498,9 +521,15 @@ static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, } return WERR_OK; - } +/* Given a filter for the domain, and one for the result, perform the + * ldb search. The format offered and desired flags change the + * behaviours, including what attributes to return. + * + * The smb_krb5_context is required because we use the krb5 libs for principal parsing + */ + static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, @@ -733,6 +762,10 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ return WERR_INVALID_PARAM; } +/* Given a user Principal Name (such as foo@bar.com), + * return the user and domain DNs. This is used in the KDC to then + * return the Keys and evaluate policy */ + NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, const char *user_principal_name, @@ -792,6 +825,10 @@ NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx, } +/* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM), + * return the user and domain DNs. This is used in the KDC to then + * return the Keys and evaluate policy */ + NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, const char *service_principal_name, diff --git a/source4/lib/ldb/common/ldb_parse.c b/source4/lib/ldb/common/ldb_parse.c index 5824a8d003..25e4b727df 100644 --- a/source4/lib/ldb/common/ldb_parse.c +++ b/source4/lib/ldb/common/ldb_parse.c @@ -127,6 +127,19 @@ char *ldb_binary_encode(void *mem_ctx, struct ldb_val val) return ret; } +/* + encode a string as a RFC2254 binary string, escaping any + non-printable or '\' characters. This routine is suitable for use + in escaping user data in ldap filters. +*/ +char *ldb_binary_encode_string(void *mem_ctx, const char *string) +{ + struct ldb_val val; + val.data = string; + val.length = strlen(string); + return ldb_binary_encode(mem_ctx, val); +} + /* find the first matching wildcard */ static char *ldb_parse_find_wildcard(char *value) { diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h index 2fdf40b3bc..9c3b033091 100644 --- a/source4/lib/ldb/include/ldb.h +++ b/source4/lib/ldb/include/ldb.h @@ -214,7 +214,7 @@ struct ldb_parse_tree { struct ldb_parse_tree *ldb_parse_tree(void *mem_ctx, const char *s); char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree); char *ldb_binary_encode(void *ctx, struct ldb_val val); - +char *ldb_binary_encode_string(void *mem_ctx, const char *string); /* functions for controlling attribute handling diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c index a0770764f5..64da9f488f 100644 --- a/source4/rpc_server/lsa/dcesrv_lsa.c +++ b/source4/rpc_server/lsa/dcesrv_lsa.c @@ -579,7 +579,7 @@ static NTSTATUS lsa_CreateTrustedDomain(struct dcesrv_call_state *dce_call, TALL ret = gendb_search(trusted_domain_state->policy->sam_ldb, mem_ctx, policy_state->system_dn, &msgs, attrs, "(&(cn=%s)(objectclass=trustedDomain))", - r->in.info->name.string); + ldb_binary_encode_string(mem_ctx, r->in.info->name.string)); if (ret > 0) { return NT_STATUS_OBJECT_NAME_COLLISION; } @@ -740,7 +740,7 @@ static NTSTATUS lsa_OpenTrustedDomainByName(struct dcesrv_call_state *dce_call, ret = gendb_search(trusted_domain_state->policy->sam_ldb, mem_ctx, policy_state->system_dn, &msgs, attrs, "(&(flatname=%s)(objectclass=trustedDomain))", - r->in.name.string); + ldb_binary_encode_string(mem_ctx, r->in.name.string)); if (ret == 0) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } @@ -1709,7 +1709,7 @@ static NTSTATUS lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX return NT_STATUS_INVALID_PARAMETER; } - name2 = talloc_asprintf(mem_ctx, "%s Secret", name); + name2 = talloc_asprintf(mem_ctx, "%s Secret", ldb_binary_encode_string(mem_ctx, name)); /* search for the secret record */ ret = gendb_search(secret_state->sam_ldb, mem_ctx, policy_state->system_dn, &msgs, attrs, @@ -1745,7 +1745,8 @@ static NTSTATUS lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX ret = gendb_search(secret_state->sam_ldb, mem_ctx, ldb_dn_explode(mem_ctx, "cn=LSA Secrets"), &msgs, attrs, - "(&(cn=%s)(objectclass=secret))", name); + "(&(cn=%s)(objectclass=secret))", + ldb_binary_encode_string(mem_ctx, name)); if (ret > 0) { return NT_STATUS_OBJECT_NAME_COLLISION; } @@ -1843,7 +1844,7 @@ static NTSTATUS lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *m ret = gendb_search(secret_state->sam_ldb, mem_ctx, policy_state->system_dn, &msgs, attrs, "(&(cn=%s Secret)(objectclass=secret))", - name); + ldb_binary_encode_string(mem_ctx, name)); if (ret == 0) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } @@ -1867,7 +1868,8 @@ static NTSTATUS lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *m ret = gendb_search(secret_state->sam_ldb, mem_ctx, ldb_dn_explode(mem_ctx, "cn=LSA Secrets"), &msgs, attrs, - "(&(cn=%s)(objectclass=secret))", name); + "(&(cn=%s)(objectclass=secret))", + ldb_binary_encode_string(mem_ctx, name)); if (ret == 0) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } @@ -2496,7 +2498,7 @@ static NTSTATUS lsa_lookup_name(struct lsa_policy_state *state, TALLOC_CTX *mem_ name = p + 1; } - ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs, "sAMAccountName=%s", name); + ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs, "sAMAccountName=%s", ldb_binary_encode_string(mem_ctx, name)); if (ret == 1) { *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid"); if (*sid == NULL) { diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c index e2b1a3bddc..3de85388dd 100644 --- a/source4/rpc_server/samr/dcesrv_samr.c +++ b/source4/rpc_server/samr/dcesrv_samr.c @@ -186,7 +186,7 @@ static NTSTATUS samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX ret = gendb_search(c_state->sam_ctx, mem_ctx, NULL, &ref_msgs, ref_attrs, "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", - r->in.domain_name->string); + ldb_binary_encode_string(mem_ctx, r->in.domain_name->string)); if (ret != 1) { return NT_STATUS_NO_SUCH_DOMAIN; } @@ -537,7 +537,7 @@ static NTSTATUS samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLO name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL, "sAMAccountName", "(&(sAMAccountName=%s)(objectclass=group))", - groupname); + ldb_binary_encode_string(mem_ctx, groupname)); if (name != NULL) { return NT_STATUS_GROUP_EXISTS; } @@ -741,7 +741,8 @@ static NTSTATUS samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX /* check if the user already exists */ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL, "sAMAccountName", - "(&(sAMAccountName=%s)(objectclass=user))", account_name); + "(&(sAMAccountName=%s)(objectclass=user))", + ldb_binary_encode_string(mem_ctx, account_name)); if (name != NULL) { return NT_STATUS_USER_EXISTS; } @@ -969,7 +970,7 @@ static NTSTATUS samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_C name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL, "sAMAccountName", "(sAMAccountName=%s)(objectclass=group))", - alias_name); + ldb_binary_encode_string(mem_ctx, alias_name)); if (name != NULL) { return NT_STATUS_ALIAS_EXISTS; @@ -1251,7 +1252,8 @@ static NTSTATUS samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX r->out.types.ids[i] = SID_NAME_UNKNOWN; count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs, - "sAMAccountName=%s", r->in.names[i].string); + "sAMAccountName=%s", + ldb_binary_encode_string(mem_ctx, r->in.names[i].string)); if (count != 1) { status = STATUS_SOME_UNMAPPED; continue; -- cgit