From a87bc35edcc6ef8b531c5bfdbf6f51f2f3d1e0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Love=20H=C3=B6rnquist=20=C3=85strand?= Date: Sat, 20 Sep 2003 00:08:06 +0000 Subject: [PATCH] add support for Set password protocol as defined by RFC3244 -- Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@12888 ec53bebd-3082-4978-b11e-865c3cabbd6b --- kpasswd/kpasswdd.c | 186 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 155 insertions(+), 31 deletions(-) diff --git a/kpasswd/kpasswdd.c b/kpasswd/kpasswdd.c index 54f91c326..95fa735a5 100644 --- a/kpasswd/kpasswdd.c +++ b/kpasswd/kpasswdd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -199,70 +199,188 @@ reply_priv (krb5_auth_context auth_context, static void change (krb5_auth_context auth_context, - krb5_principal principal, + krb5_principal admin_principal, + u_int16_t version, int s, struct sockaddr *sa, int sa_size, - krb5_data *pwd_data) + krb5_data *in_data) { krb5_error_code ret; - char *client; + char *client = NULL, *admin = NULL; const char *pwd_reason; kadm5_config_params conf; - void *kadm5_handle; + void *kadm5_handle = NULL; + krb5_principal principal; + krb5_data *pwd_data = NULL; char *tmp; + ChangePasswdDataMS chpw; memset (&conf, 0, sizeof(conf)); + memset(&chpw, 0, sizeof(chpw)); - krb5_unparse_name (context, principal, &client); + if (version == KRB5_KPASSWD_VERS_CHANGEPW) { + 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"); + 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 (ret) + abort(); + if (*chpw.targrealm == NULL) + free(princ.realm); + } 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; + } ret = kadm5_init_with_password_ctx(context, - client, + admin, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm5_handle); if (ret) { - free (client); krb5_warn (context, ret, "kadm5_init_with_password_ctx"); reply_priv (auth_context, s, sa, sa_size, 2, "Internal error"); - return; + goto out; } - krb5_warnx (context, "Changing password for %s", client); - free (client); - - pwd_reason = kadm5_check_password_quality (context, principal, pwd_data); - if (pwd_reason != NULL ) { - krb5_warnx (context, "%s", pwd_reason); - reply_priv (auth_context, s, sa, sa_size, 4, pwd_reason); - kadm5_destroy (kadm5_handle); - return; + 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; } - tmp = malloc (pwd_data->length + 1); - if (tmp == NULL) { - krb5_warnx (context, "malloc: out of memory"); - reply_priv (auth_context, s, sa, sa_size, 2, + /* + * 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; } - memcpy (tmp, pwd_data->data, pwd_data->length); - tmp[pwd_data->length] = '\0'; + tmp = pwd_data->data; + tmp[pwd_data->length - 1] = '\0'; ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp); - memset (tmp, 0, pwd_data->length); - free (tmp); + krb5_free_data (context, pwd_data); + pwd_data = NULL; if (ret) { krb5_warn (context, ret, "kadm5_s_chpass_principal_cond"); - reply_priv (auth_context, s, sa, sa_size, 2, + reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR, "Internal error"); goto out; } - reply_priv (auth_context, s, sa, sa_size, 0, "Password changed"); + reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS, + "Password changed"); out: - kadm5_destroy (kadm5_handle); + free_ChangePasswdDataMS(&chpw); + 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 int @@ -271,6 +389,7 @@ verify (krb5_auth_context *auth_context, krb5_keytab keytab, krb5_ticket **ticket, krb5_data *out_data, + u_int16_t *version, int s, struct sockaddr *sa, int sa_size, @@ -291,11 +410,13 @@ verify (krb5_auth_context *auth_context, reply_error (server, s, sa, sa_size, 0, 1, "Bad request"); return 1; } - if (pkt_ver != 0x0001) { + if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW && + pkt_ver != KRB5_KPASSWD_VERS_SETPW) { krb5_warnx (context, "Bad version (%d)", pkt_ver); reply_error (server, s, sa, sa_size, 0, 1, "Wrong program version"); return 1; } + *version = pkt_ver; ap_req_data.data = msg + 6; ap_req_data.length = ap_req_len; @@ -361,6 +482,8 @@ process (krb5_principal server, krb5_data out_data; krb5_ticket *ticket; krb5_address other_addr; + u_int16_t version; + krb5_data_zero (&out_data); @@ -390,9 +513,10 @@ process (krb5_principal server, } if (verify (&auth_context, server, keytab, &ticket, &out_data, - s, sa, sa_size, msg, len) == 0) { + &version, s, sa, sa_size, msg, len) == 0) { change (auth_context, ticket->client, + version, s, sa, sa_size, &out_data);