s4-kdc: Add new kpasswd service Heimdal backend
authorAndreas Schneider <asn@samba.org>
Wed, 7 Sep 2016 14:03:15 +0000 (16:03 +0200)
committerJeremy Allison <jra@samba.org>
Mon, 12 Sep 2016 22:19:25 +0000 (00:19 +0200)
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source4/kdc/kpasswd-service-heimdal.c [new file with mode: 0644]
source4/kdc/wscript_build

diff --git a/source4/kdc/kpasswd-service-heimdal.c b/source4/kdc/kpasswd-service-heimdal.c
new file mode 100644 (file)
index 0000000..12c7402
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Samba kpasswd implementation
+
+   Copyright (c) 2016      Andreas Schneider <asn@samba.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "param/param.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "kdc/kdc-server.h"
+#include "kdc/kpasswd_glue.h"
+#include "kdc/kpasswd-service.h"
+#include "kdc/kpasswd-helper.h"
+
+static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+                                              TALLOC_CTX *mem_ctx,
+                                              struct auth_session_info *session_info,
+                                              DATA_BLOB *password,
+                                              DATA_BLOB *kpasswd_reply,
+                                              const char **error_string)
+{
+       NTSTATUS status;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       enum samPwdChangeReason reject_reason;
+       const char *reject_string = NULL;
+       struct samr_DomInfo1 *dominfo;
+       bool ok;
+
+       status = samdb_kpasswd_change_password(mem_ctx,
+                                              kdc->task->lp_ctx,
+                                              kdc->task->event_ctx,
+                                              kdc->samdb,
+                                              session_info,
+                                              password,
+                                              &reject_reason,
+                                              &dominfo,
+                                              &reject_string,
+                                              &result);
+       if (!NT_STATUS_IS_OK(status)) {
+               ok = kpasswd_make_error_reply(mem_ctx,
+                                             KRB5_KPASSWD_ACCESSDENIED,
+                                             reject_string,
+                                             kpasswd_reply);
+               if (!ok) {
+                       *error_string = "Failed to create reply";
+                       return KRB5_KPASSWD_HARDERROR;
+               }
+               /* We want to send an an authenticated packet. */
+               return 0;
+       }
+
+       ok = kpasswd_make_pwchange_reply(mem_ctx,
+                                        result,
+                                        reject_reason,
+                                        dominfo,
+                                        kpasswd_reply);
+       if (!ok) {
+               *error_string = "Failed to create reply";
+               return KRB5_KPASSWD_HARDERROR;
+       }
+
+       return 0;
+}
+
+static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct auth_session_info *session_info,
+                                           DATA_BLOB *decoded_data,
+                                           DATA_BLOB *kpasswd_reply,
+                                           const char **error_string)
+{
+       krb5_context context = kdc->smb_krb5_context->krb5_context;
+       krb5_error_code code;
+       krb5_principal target_principal;
+       ChangePasswdDataMS chpw = {};
+       size_t chpw_len = 0;
+       DATA_BLOB password = data_blob_null;
+       enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+       struct samr_DomInfo1 *dominfo = NULL;
+       char *target_principal_string = NULL;
+       bool is_service_principal = false;
+       NTSTATUS status;
+       bool ok;
+
+       code = decode_ChangePasswdDataMS(decoded_data->data,
+                                        decoded_data->length,
+                                        &chpw,
+                                        &chpw_len);
+       if (code != 0) {
+               DBG_WARNING("decode_ChangePasswdDataMS failed\n");
+               ok = kpasswd_make_error_reply(mem_ctx,
+                                             KRB5_KPASSWD_MALFORMED,
+                                             "Failed to decode packet",
+                                             kpasswd_reply);
+               if (!ok) {
+                       *error_string = "Failed to create reply";
+                       return KRB5_KPASSWD_HARDERROR;
+               }
+               return 0;
+       }
+
+       ok = convert_string_talloc_handle(mem_ctx,
+                                         lpcfg_iconv_handle(kdc->task->lp_ctx),
+                                         CH_UTF8,
+                                         CH_UTF16,
+                                         (const char *)chpw.newpasswd.data,
+                                         chpw.newpasswd.length,
+                                         (void **)&password.data,
+                                         &password.length);
+       if (!ok) {
+               free_ChangePasswdDataMS(&chpw);
+               DBG_WARNING("String conversion failed\n");
+               *error_string = "String conversion failed";
+               return KRB5_KPASSWD_HARDERROR;
+       }
+
+       if ((chpw.targname != NULL && chpw.targrealm == NULL) ||
+           (chpw.targname == NULL && chpw.targrealm != NULL)) {
+               free_ChangePasswdDataMS(&chpw);
+               ok = kpasswd_make_error_reply(mem_ctx,
+                                             KRB5_KPASSWD_MALFORMED,
+                                             "Realm and principal must be "
+                                             "both present, or neither present",
+                                             kpasswd_reply);
+               if (!ok) {
+                       *error_string = "Failed to create reply";
+                       return KRB5_KPASSWD_HARDERROR;
+               }
+               return 0;
+       }
+
+       if (chpw.targname != NULL && chpw.targrealm != NULL) {
+               code = krb5_build_principal_ext(context,
+                                              &target_principal,
+                                              strlen(*chpw.targrealm),
+                                              *chpw.targrealm,
+                                              0);
+               if (code != 0) {
+                       free_ChangePasswdDataMS(&chpw);
+                       return kpasswd_make_error_reply(mem_ctx,
+                                                       KRB5_KPASSWD_MALFORMED,
+                                                       "Failed to parse principal",
+                                                       kpasswd_reply);
+               }
+               code = copy_PrincipalName(chpw.targname,
+                                         &target_principal->name);
+               if (code != 0) {
+                       free_ChangePasswdDataMS(&chpw);
+                       krb5_free_principal(context, target_principal);
+                       return kpasswd_make_error_reply(mem_ctx,
+                                                       KRB5_KPASSWD_MALFORMED,
+                                                       "Failed to parse principal",
+                                                       kpasswd_reply);
+               }
+       } else {
+               free_ChangePasswdDataMS(&chpw);
+               return kpasswd_change_password(kdc,
+                                              mem_ctx,
+                                              session_info,
+                                              &password,
+                                              kpasswd_reply,
+                                              error_string);
+       }
+       free_ChangePasswdDataMS(&chpw);
+
+       if (target_principal->name.name_string.len >= 2) {
+               is_service_principal = true;
+
+               code = krb5_unparse_name_short(context,
+                                              target_principal,
+                                              &target_principal_string);
+       } else {
+               code = krb5_unparse_name(context,
+                                        target_principal,
+                                        &target_principal_string);
+       }
+       krb5_free_principal(context, target_principal);
+       if (code != 0) {
+               ok = kpasswd_make_error_reply(mem_ctx,
+                                             KRB5_KPASSWD_MALFORMED,
+                                             "Failed to parse principal",
+                                             kpasswd_reply);
+               if (!ok) {
+                       *error_string = "Failed to create reply";
+                       return KRB5_KPASSWD_HARDERROR;
+               }
+       }
+
+       status = kpasswd_samdb_set_password(mem_ctx,
+                                           kdc->task->event_ctx,
+                                           kdc->task->lp_ctx,
+                                           session_info,
+                                           is_service_principal,
+                                           target_principal_string,
+                                           &password,
+                                           &reject_reason,
+                                           &dominfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("kpasswd_samdb_set_password failed - %s\n",
+                       nt_errstr(status));
+       }
+
+       ok = kpasswd_make_pwchange_reply(mem_ctx,
+                                        status,
+                                        reject_reason,
+                                        dominfo,
+                                        kpasswd_reply);
+       if (!ok) {
+               *error_string = "Failed to create reply";
+               return KRB5_KPASSWD_HARDERROR;
+       }
+
+       return 0;
+}
+
+krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+                                      TALLOC_CTX *mem_ctx,
+                                      struct gensec_security *gensec_security,
+                                      uint16_t verno,
+                                      DATA_BLOB *decoded_data,
+                                      DATA_BLOB *kpasswd_reply,
+                                      const char **error_string)
+{
+       struct auth_session_info *session_info;
+       NTSTATUS status;
+
+       status = gensec_session_info(gensec_security,
+                                    mem_ctx,
+                                    &session_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "gensec_session_info failed - %s",
+                                               nt_errstr(status));
+               return KRB5_KPASSWD_HARDERROR;
+       }
+
+       switch(verno) {
+       case KRB5_KPASSWD_VERS_CHANGEPW: {
+               DATA_BLOB password = data_blob_null;
+               bool ok;
+
+               ok = convert_string_talloc_handle(mem_ctx,
+                                                 lpcfg_iconv_handle(kdc->task->lp_ctx),
+                                                 CH_UTF8,
+                                                 CH_UTF16,
+                                                 (const char *)decoded_data->data,
+                                                 decoded_data->length,
+                                                 (void **)&password.data,
+                                                 &password.length);
+               if (!ok) {
+                       *error_string = "String conversion failed!";
+                       DBG_WARNING("%s\n", *error_string);
+                       return KRB5_KPASSWD_HARDERROR;
+               }
+
+               return kpasswd_change_password(kdc,
+                                              mem_ctx,
+                                              session_info,
+                                              &password,
+                                              kpasswd_reply,
+                                              error_string);
+       }
+       case KRB5_KPASSWD_VERS_SETPW: {
+               return kpasswd_set_password(kdc,
+                                           mem_ctx,
+                                           session_info,
+                                           decoded_data,
+                                           kpasswd_reply,
+                                           error_string);
+       }
+       default:
+               *error_string = talloc_asprintf(mem_ctx,
+                                               "Protocol version %u not supported",
+                                               verno);
+               return KRB5_KPASSWD_BAD_VERSION;
+       }
+
+       return 0;
+}
index 99678ea441d2fc6c1f86d75fb41d7f74780d0205..18eae904872be3faaf44c9283e4b8ae6473cf1e8 100755 (executable)
@@ -54,8 +54,12 @@ bld.SAMBA_SUBSYSTEM('KDC-SERVER',
                     ''',
                     enabled=bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'))
 
+kpasswd_flavor_src = 'kpasswd-service.c kpasswd-helper.c'
+if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'):
+    kpasswd_flavor_src = kpasswd_flavor_src + ' kpasswd-service-heimdal.c'
+
 bld.SAMBA_SUBSYSTEM('KPASSWD-SERVICE',
-                    source='kpasswd-service.c kpasswd-helper.c',
+                    source=kpasswd_flavor_src,
                     includes=kdc_include,
                     deps='''
                          krb5samba