/** * Obtain the client principal for this credentials context. * @param cred credentials context * @retval The username set on this context. * @note Return value will never be NULL except by programmer error. */ _PUBLIC_ const char *cli_credentials_get_principal_and_obtained(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, enum credentials_obtained *obtained) { if (cred->machine_account_pending) { cli_credentials_set_machine_account(cred, cred->machine_account_pending_lp_ctx); } if (cred->principal_obtained == CRED_CALLBACK && !cred->callback_running) { cred->callback_running = true; cred->principal = cred->principal_cb(cred); cred->callback_running = false; if (cred->principal_obtained == CRED_CALLBACK) { cred->principal_obtained = CRED_CALLBACK_RESULT; cli_credentials_invalidate_ccache(cred, cred->principal_obtained); } } if (cred->principal_obtained < cred->username_obtained || cred->principal_obtained < MAX(cred->domain_obtained, cred->realm_obtained)) { if (cred->domain_obtained > cred->realm_obtained) { *obtained = MIN(cred->domain_obtained, cred->username_obtained); return talloc_asprintf(mem_ctx, "%s@%s", cli_credentials_get_username(cred), cli_credentials_get_domain(cred)); } else { *obtained = MIN(cred->domain_obtained, cred->username_obtained); return talloc_asprintf(mem_ctx, "%s@%s", cli_credentials_get_username(cred), cli_credentials_get_realm(cred)); } } *obtained = cred->principal_obtained; return talloc_strdup(mem_ctx, cred->principal); }
/** * Fill in credentials for a particular principal, from the secrets database. * * @param cred Credentials structure to fill in * @retval NTSTATUS error detailing any failure */ _PUBLIC_ NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred, struct loadparm_context *lp_ctx, const char *serviceprincipal) { NTSTATUS status; char *filter; char *error_string = NULL; /* Bleh, nasty recursion issues: We are setting a machine * account here, so we don't want the 'pending' flag around * any more */ cred->machine_account_pending = false; filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH, cli_credentials_get_realm(cred), cli_credentials_get_domain(cred), serviceprincipal); status = cli_credentials_set_secrets_lct(cred, lp_ctx, NULL, SECRETS_PRINCIPALS_DN, filter, 0, NULL, &error_string); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not find %s principal in secrets database: %s: %s\n", serviceprincipal, nt_errstr(status), error_string ? error_string : "<no error>")); } return status; }
/** * Fill in credentials for the machine trust account, from the secrets database. * * @param cred Credentials structure to fill in * @retval NTSTATUS error detailing any failure */ NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred) { char *filter; /* Bleh, nasty recursion issues: We are setting a machine * account here, so we don't want the 'pending' flag around * any more */ cred->machine_account_pending = False; filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH, cli_credentials_get_realm(cred), cli_credentials_get_domain(cred)); return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN, filter); }
static bool test_parse_string(struct torture_context *tctx) { struct cli_credentials *creds = cli_credentials_init_anon(tctx); /* anonymous */ cli_credentials_parse_string(creds, "%", CRED_SPECIFIED); torture_assert_str_equal(tctx, cli_credentials_get_domain(creds), "", "domain"); torture_assert_str_equal(tctx, cli_credentials_get_username(creds), "", "username"); torture_assert(tctx, cli_credentials_get_password(creds) == NULL, "password"); /* username + password */ cli_credentials_parse_string(creds, "somebody%secret", CRED_SPECIFIED); torture_assert_str_equal(tctx, cli_credentials_get_domain(creds), "", "domain"); torture_assert_str_equal(tctx, cli_credentials_get_username(creds), "somebody", "username"); torture_assert_str_equal(tctx, cli_credentials_get_password(creds), "secret", "password"); /* principal */ cli_credentials_parse_string(creds, "prin@styx", CRED_SPECIFIED); torture_assert_str_equal(tctx, cli_credentials_get_realm(creds), "STYX", "realm"); torture_assert_str_equal(tctx, cli_credentials_get_principal(creds, tctx), "prin@styx", "principal"); return true; }
/** * Fill in credentials for the machine trust account, from the secrets database. * * @param cred Credentials structure to fill in * @retval NTSTATUS error detailing any failure */ NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred, struct tevent_context *event_ctx, struct loadparm_context *lp_ctx) { NTSTATUS status; char *filter; /* Bleh, nasty recursion issues: We are setting a machine * account here, so we don't want the 'pending' flag around * any more */ cred->machine_account_pending = false; filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH, cli_credentials_get_realm(cred), cli_credentials_get_domain(cred)); status = cli_credentials_set_secrets(cred, event_ctx, lp_ctx, NULL, SECRETS_PRINCIPALS_DN, filter); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Could not find krbtgt (master Kerberos) account in secrets database: %s", nt_errstr(status))); } return status; }
static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) { struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *creds = gensec_get_credentials(gensec_security); NTSTATUS nt_status; gss_buffer_desc name_token; gss_OID name_type; OM_uint32 maj_stat, min_stat; const char *hostname = gensec_get_target_hostname(gensec_security); if (!hostname) { DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_ipaddress(hostname)) { DEBUG(2, ("Cannot do GSSAPI to an IP address\n")); return NT_STATUS_INVALID_PARAMETER; } if (strcmp(hostname, "localhost") == 0) { DEBUG(2, ("GSSAPI to 'localhost' does not make sense\n")); return NT_STATUS_INVALID_PARAMETER; } nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); if (cli_credentials_get_impersonate_principal(creds)) { gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); } gensec_gssapi_state->target_principal = gensec_get_target_principal(gensec_security); if (gensec_gssapi_state->target_principal) { name_type = GSS_C_NULL_OID; } else { gensec_gssapi_state->target_principal = talloc_asprintf(gensec_gssapi_state, "%s/%s@%s", gensec_get_target_service(gensec_security), hostname, cli_credentials_get_realm(creds)); name_type = GSS_C_NT_USER_NAME; } name_token.value = discard_const_p(uint8_t, gensec_gssapi_state->target_principal); name_token.length = strlen(gensec_gssapi_state->target_principal); maj_stat = gss_import_name (&min_stat, &name_token, name_type, &gensec_gssapi_state->server_name); if (maj_stat) { DEBUG(2, ("GSS Import name of %s failed: %s\n", (char *)name_token.value, gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; } return NT_STATUS_OK; }
/* Get the keytab (actually, a container containing the krb5_keytab) * attached to this context. If this hasn't been done or set before, * it will be generated from the password. */ _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, struct loadparm_context *lp_ctx, struct keytab_container **_ktc) { krb5_error_code ret; struct keytab_container *ktc; struct smb_krb5_context *smb_krb5_context; const char *keytab_name; krb5_keytab keytab; TALLOC_CTX *mem_ctx; if (cred->keytab_obtained >= (MAX(cred->principal_obtained, cred->username_obtained))) { *_ktc = cred->keytab; return 0; } if (cli_credentials_is_anonymous(cred)) { return EINVAL; } ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); if (ret) { return ret; } mem_ctx = talloc_new(cred); if (!mem_ctx) { return ENOMEM; } ret = smb_krb5_create_memory_keytab(mem_ctx, smb_krb5_context->krb5_context, cli_credentials_get_password(cred), cli_credentials_get_username(cred), cli_credentials_get_realm(cred), cli_credentials_get_kvno(cred), &keytab, &keytab_name); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab, keytab_name, &ktc); if (ret) { talloc_free(mem_ctx); return ret; } cred->keytab_obtained = (MAX(cred->principal_obtained, cred->username_obtained)); /* We make this keytab up based on a password. Therefore * match-by-key is acceptable, we can't match on the wrong * principal */ ktc->password_based = true; talloc_steal(cred, ktc); cred->keytab = ktc; *_ktc = cred->keytab; talloc_free(mem_ctx); return ret; }
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct schannel_state *state = (struct schannel_state *)gensec_security->private_data; NTSTATUS status; enum ndr_err_code ndr_err; struct NL_AUTH_MESSAGE bind_schannel; struct NL_AUTH_MESSAGE bind_schannel_ack; struct netlogon_creds_CredentialState *creds; const char *workstation; const char *domain; *out = data_blob(NULL, 0); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: if (state->state != SCHANNEL_STATE_START) { /* we could parse the bind ack, but we don't know what it is yet */ return NT_STATUS_OK; } state->creds = cli_credentials_get_netlogon_creds(gensec_security->credentials); if (state->creds == NULL) { return NT_STATUS_INVALID_PARAMETER_MIX; } /* * We need to create a reference here or we don't get * updates performed on the credentials if we create a * copy. */ state->creds = talloc_reference(state, state->creds); if (state->creds == NULL) { return NT_STATUS_NO_MEMORY; } bind_schannel.MessageType = NL_NEGOTIATE_REQUEST; #if 0 /* to support this we'd need to have access to the full domain name */ /* 0x17, 23 */ bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME | NL_FLAG_OEM_NETBIOS_COMPUTER_NAME | NL_FLAG_UTF8_DNS_DOMAIN_NAME | NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME; bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials); bind_schannel.oem_netbios_computer.a = cli_credentials_get_workstation(gensec_security->credentials); bind_schannel.utf8_dns_domain = cli_credentials_get_realm(gensec_security->credentials); /* w2k3 refuses us if we use the full DNS workstation? why? perhaps because we don't fill in the dNSHostName attribute in the machine account? */ bind_schannel.utf8_netbios_computer = cli_credentials_get_workstation(gensec_security->credentials); #else bind_schannel.Flags = NL_FLAG_OEM_NETBIOS_DOMAIN_NAME | NL_FLAG_OEM_NETBIOS_COMPUTER_NAME; bind_schannel.oem_netbios_domain.a = cli_credentials_get_domain(gensec_security->credentials); bind_schannel.oem_netbios_computer.a = cli_credentials_get_workstation(gensec_security->credentials); #endif ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel, (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not create schannel bind: %s\n", nt_errstr(status))); return status; } state->state = SCHANNEL_STATE_UPDATE_1; return NT_STATUS_MORE_PROCESSING_REQUIRED; case GENSEC_SERVER: if (state->state != SCHANNEL_STATE_START) { /* no third leg on this protocol */ return NT_STATUS_INVALID_PARAMETER; } /* parse the schannel startup blob */ ndr_err = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, (ndr_pull_flags_fn_t)ndr_pull_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not parse incoming schannel bind: %s\n", nt_errstr(status))); return status; } if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_DOMAIN_NAME) { domain = bind_schannel.oem_netbios_domain.a; if (strcasecmp_m(domain, lpcfg_workgroup(gensec_security->settings->lp_ctx)) != 0) { DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n", domain, lpcfg_workgroup(gensec_security->settings->lp_ctx))); return NT_STATUS_LOGON_FAILURE; } } else if (bind_schannel.Flags & NL_FLAG_UTF8_DNS_DOMAIN_NAME) { domain = bind_schannel.utf8_dns_domain.u; if (strcasecmp_m(domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx)) != 0) { DEBUG(3, ("Request for schannel to incorrect domain: %s != our domain %s\n", domain, lpcfg_dnsdomain(gensec_security->settings->lp_ctx))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(3, ("Request for schannel to without domain\n")); return NT_STATUS_LOGON_FAILURE; } if (bind_schannel.Flags & NL_FLAG_OEM_NETBIOS_COMPUTER_NAME) { workstation = bind_schannel.oem_netbios_computer.a; } else if (bind_schannel.Flags & NL_FLAG_UTF8_NETBIOS_COMPUTER_NAME) { workstation = bind_schannel.utf8_netbios_computer.u; } else { DEBUG(3, ("Request for schannel to without netbios workstation\n")); return NT_STATUS_LOGON_FAILURE; } status = schannel_get_creds_state(out_mem_ctx, gensec_security->settings->lp_ctx, workstation, &creds); if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n", workstation, nt_errstr(status))); if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { return NT_STATUS_LOGON_FAILURE; } return status; } state->creds = talloc_steal(state, creds); bind_schannel_ack.MessageType = NL_NEGOTIATE_RESPONSE; bind_schannel_ack.Flags = 0; bind_schannel_ack.Buffer.dummy = 0x6c0000; /* actually I think * this does not have * any meaning here * - gd */ ndr_err = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n", workstation, nt_errstr(status))); return status; } state->state = SCHANNEL_STATE_UPDATE_1; return NT_STATUS_OK; } return NT_STATUS_INVALID_PARAMETER; }
static PyObject *py_creds_get_realm(PyObject *self, PyObject *unused) { return PyString_FromStringOrNULL(cli_credentials_get_realm(PyCredentials_AsCliCredentials(self))); }
/* Get the keytab (actually, a container containing the krb5_keytab) * attached to this context. If this hasn't been done or set before, * it will be generated from the password. */ _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, struct loadparm_context *lp_ctx, struct keytab_container **_ktc) { krb5_error_code ret; struct keytab_container *ktc; struct smb_krb5_context *smb_krb5_context; const char *keytab_name; krb5_keytab keytab; TALLOC_CTX *mem_ctx; const char *username = cli_credentials_get_username(cred); const char *realm = cli_credentials_get_realm(cred); const char *error_string; const char *salt_principal; if (cred->keytab_obtained >= (MAX(cred->principal_obtained, cred->username_obtained))) { *_ktc = cred->keytab; return 0; } if (cli_credentials_is_anonymous(cred)) { return EINVAL; } ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context); if (ret) { return ret; } mem_ctx = talloc_new(cred); if (!mem_ctx) { return ENOMEM; } /* * FIXME: Currently there is no better way than to create the correct * salt principal by checking if the username ends with a '$'. It would * be better if it is part of the credentials. */ ret = smb_krb5_create_salt_principal(mem_ctx, username, realm, &salt_principal, &error_string); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_create_memory_keytab(mem_ctx, smb_krb5_context->krb5_context, cli_credentials_get_password(cred), username, realm, salt_principal, cli_credentials_get_kvno(cred), &keytab, &keytab_name); if (ret) { talloc_free(mem_ctx); return ret; } ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context, keytab, keytab_name, &ktc); if (ret) { talloc_free(mem_ctx); return ret; } cred->keytab_obtained = (MAX(cred->principal_obtained, cred->username_obtained)); /* We make this keytab up based on a password. Therefore * match-by-key is acceptable, we can't match on the wrong * principal */ ktc->password_based = true; talloc_steal(cred, ktc); cred->keytab = ktc; *_ktc = cred->keytab; talloc_free(mem_ctx); return ret; }