static void change (krb5_auth_context auth_context, krb5_principal admin_principal, uint16_t version, int s, struct sockaddr *sa, int sa_size, krb5_data *in_data) { krb5_error_code ret; char *client = NULL, *admin = NULL; const char *pwd_reason; kadm5_config_params conf; void *kadm5_handle = NULL; krb5_principal principal = NULL; krb5_data *pwd_data = NULL; char *tmp; ChangePasswdDataMS chpw; memset (&conf, 0, sizeof(conf)); memset(&chpw, 0, sizeof(chpw)); if (version == KRB5_KPASSWD_VERS_CHANGEPW) { ret = krb5_copy_data(context, in_data, &pwd_data); if (ret) { krb5_warn (context, ret, "krb5_copy_data"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "out out memory copying password"); return; } principal = admin_principal; } else if (version == KRB5_KPASSWD_VERS_SETPW) { size_t len; ret = decode_ChangePasswdDataMS(in_data->data, in_data->length, &chpw, &len); if (ret) { krb5_warn (context, ret, "decode_ChangePasswdDataMS"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "malformed ChangePasswdData"); return; } ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data); if (ret) { krb5_warn (context, ret, "krb5_copy_data"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "out out memory copying password"); goto out; } if (chpw.targname == NULL && chpw.targrealm != NULL) { krb5_warn (context, ret, "kadm5_init_with_password_ctx"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED, "targrealm but not targname"); goto out; } if (chpw.targname) { krb5_principal_data princ; princ.name = *chpw.targname; princ.realm = *chpw.targrealm; if (princ.realm == NULL) { ret = krb5_get_default_realm(context, &princ.realm); if (ret) { krb5_warnx (context, "kadm5_init_with_password_ctx: " "failed to allocate realm"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, "failed to allocate realm"); goto out; } } ret = krb5_copy_principal(context, &princ, &principal); if (*chpw.targrealm == NULL) free(princ.realm); if (ret) { krb5_warn(context, ret, "krb5_copy_principal"); reply_priv(auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "failed to allocate principal"); goto out; } } else principal = admin_principal; } else { krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "Unknown protocol used"); return; } ret = krb5_unparse_name (context, admin_principal, &admin); if (ret) { krb5_warn (context, ret, "unparse_name failed"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "out of memory error"); goto out; } conf.realm = principal->realm; conf.mask |= KADM5_CONFIG_REALM; ret = kadm5_init_with_password_ctx(context, admin, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm5_handle); if (ret) { krb5_warn (context, ret, "kadm5_init_with_password_ctx"); reply_priv (auth_context, s, sa, sa_size, 2, "Internal error"); goto out; } ret = krb5_unparse_name(context, principal, &client); if (ret) { krb5_warn (context, ret, "unparse_name failed"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "out of memory error"); goto out; } /* * Check password quality if not changing as administrator */ if (krb5_principal_compare(context, admin_principal, principal) == TRUE) { pwd_reason = kadm5_check_password_quality (context, principal, pwd_data); if (pwd_reason != NULL ) { krb5_warnx (context, "%s didn't pass password quality check with error: %s", client, pwd_reason); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, pwd_reason); goto out; } krb5_warnx (context, "Changing password for %s", client); } else { ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW, principal); if (ret) { krb5_warn (context, ret, "Check ACL failed for %s for changing %s password", admin, client); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "permission denied"); goto out; } krb5_warnx (context, "%s is changing password for %s", admin, client); } ret = krb5_data_realloc(pwd_data, pwd_data->length + 1); if (ret) { krb5_warn (context, ret, "malloc: out of memory"); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "Internal error"); goto out; } tmp = pwd_data->data; tmp[pwd_data->length - 1] = '\0'; ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp); krb5_free_data (context, pwd_data); pwd_data = NULL; if (ret) { const char *str = krb5_get_error_message(context, ret); krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str); reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR, str ? str : "Internal error"); krb5_free_error_message(context, str); goto out; } reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS, "Password changed"); out: free_ChangePasswdDataMS(&chpw); if (principal != admin_principal) krb5_free_principal(context, principal); if (admin) free(admin); if (client) free(client); if (pwd_data) krb5_free_data(context, pwd_data); if (kadm5_handle) kadm5_destroy (kadm5_handle); }
static bool kpasswd_process_request(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, struct gensec_security *gensec_security, uint16_t version, DATA_BLOB *input, DATA_BLOB *reply) { struct auth_session_info *session_info; size_t pw_len; if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security, &session_info))) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, "gensec_session_info failed!", reply); } switch (version) { case KRB5_KPASSWD_VERS_CHANGEPW: { DATA_BLOB password; if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)input->data, input->length, (void **)&password.data, &pw_len, false)) { return false; } password.length = pw_len; return kpasswdd_change_password(kdc, mem_ctx, session_info, &password, reply); break; } case KRB5_KPASSWD_VERS_SETPW: { NTSTATUS status; enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR; struct samr_DomInfo1 *dominfo = NULL; struct ldb_context *samdb; struct ldb_message *msg; krb5_context context = kdc->smb_krb5_context->krb5_context; ChangePasswdDataMS chpw; DATA_BLOB password; krb5_principal principal; char *set_password_on_princ; struct ldb_dn *set_password_on_dn; size_t len; int ret; msg = ldb_msg_new(mem_ctx); if (!msg) { return false; } ret = decode_ChangePasswdDataMS(input->data, input->length, &chpw, &len); if (ret) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "failed to decode password change structure", reply); } if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), CH_UTF8, CH_UTF16, (const char *)chpw.newpasswd.data, chpw.newpasswd.length, (void **)&password.data, &pw_len, false)) { free_ChangePasswdDataMS(&chpw); return false; } password.length = pw_len; if ((chpw.targname && !chpw.targrealm) || (!chpw.targname && chpw.targrealm)) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "Realm and principal must be both present, or neither present", reply); } if (chpw.targname && chpw.targrealm) { #ifdef SAMBA4_INTERNAL_HEIMDAL if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context, &principal, *chpw.targname, *chpw.targrealm) != 0) { free_ChangePasswdDataMS(&chpw); return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "failed to extract principal to set", reply); } #else /* SAMBA4_INTERNAL_HEIMDAL */ return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_BAD_VERSION, "Operation Not Implemented", reply); #endif /* SAMBA4_INTERNAL_HEIMDAL */ } else { free_ChangePasswdDataMS(&chpw); return kpasswdd_change_password(kdc, mem_ctx, session_info, &password, reply); } free_ChangePasswdDataMS(&chpw); if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) { krb5_free_principal(context, principal); return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_MALFORMED, "krb5_unparse_name failed!", reply); } krb5_free_principal(context, principal); samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info); if (!samdb) { return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, "Unable to open database!", reply); } DEBUG(3, ("%s\\%s (%s) is changing password of %s\n", session_info->server_info->domain_name, session_info->server_info->account_name, dom_sid_string(mem_ctx, session_info->security_token->user_sid), set_password_on_princ)); ret = ldb_transaction_start(samdb); if (ret) { status = NT_STATUS_TRANSACTION_ABORTED; return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, SAM_PWD_CHANGE_NO_ERROR, NULL, reply); } status = crack_user_principal_name(samdb, mem_ctx, set_password_on_princ, &set_password_on_dn, NULL); free(set_password_on_princ); if (!NT_STATUS_IS_OK(status)) { ldb_transaction_cancel(samdb); return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, SAM_PWD_CHANGE_NO_ERROR, NULL, reply); } msg = ldb_msg_new(mem_ctx); if (msg == NULL) { ldb_transaction_cancel(samdb); status = NT_STATUS_NO_MEMORY; } else { msg->dn = ldb_dn_copy(msg, set_password_on_dn); if (!msg->dn) { status = NT_STATUS_NO_MEMORY; } } if (NT_STATUS_IS_OK(status)) { /* Admin password set */ status = samdb_set_password(samdb, mem_ctx, set_password_on_dn, NULL, msg, &password, NULL, NULL, false, /* this is not a user password change */ &reject_reason, &dominfo); } if (NT_STATUS_IS_OK(status)) { /* modify the samdb record */ ret = samdb_replace(samdb, mem_ctx, msg); if (ret != 0) { DEBUG(2,("Failed to modify record to set password on %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(samdb))); status = NT_STATUS_ACCESS_DENIED; } } if (NT_STATUS_IS_OK(status)) { ret = ldb_transaction_commit(samdb); if (ret != 0) { DEBUG(1,("Failed to commit transaction to set password on %s: %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(samdb))); status = NT_STATUS_TRANSACTION_ABORTED; } } else { ldb_transaction_cancel(samdb); } return kpasswd_make_pwchange_reply(kdc, mem_ctx, status, reject_reason, dominfo, reply); } default: return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_BAD_VERSION, talloc_asprintf(mem_ctx, "Protocol version %u not supported", version), reply); } return true; }