s4-kdc: Migrate to tsocket_address.
[ira/wip.git] / source4 / kdc / kpasswdd.c
index d6ced4587ae3409cc56c2126567e9e8cca8374cf..18adf0a248774c908cb75b02617c338c88e4e16c 100644 (file)
@@ -8,7 +8,7 @@
 
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "smbd/service_task.h"
 #include "lib/events/events.h"
 #include "lib/socket/socket.h"
-#include "kdc/kdc.h"
+#include "lib/tsocket/tsocket.h"
 #include "system/network.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
 #include "lib/ldb/include/ldb.h"
-#include "heimdal/lib/krb5/krb5_locl.h"
-#include "heimdal/lib/krb5/krb5-private.h"
 #include "auth/gensec/gensec.h"
 #include "auth/credentials/credentials.h"
 #include "auth/credentials/credentials_krb5.h"
 #include "rpc_server/dcerpc_server.h"
 #include "rpc_server/samr/proto.h"
 #include "libcli/security/security.h"
+#include "param/param.h"
+#include "kdc/kdc.h"
 
-/* hold information about one kdc socket */
-struct kpasswd_socket {
-       struct socket_context *sock;
-       struct kdc_server *kdc;
-       struct fd_event *fde;
-
-       /* a queue of outgoing replies that have been deferred */
-       struct kdc_reply *send_queue;
-};
+/* TODO: remove all SAMBA4_INTERNAL_HEIMDAL stuff from this file */
+#ifdef SAMBA4_INTERNAL_HEIMDAL
+#include "heimdal_build/kpasswdd-glue.h"
+#endif
 
 /* Return true if there is a valid error packet formed in the error_blob */
-static BOOL kpasswdd_make_error_reply(struct kdc_server *kdc, 
+static bool kpasswdd_make_error_reply(struct kdc_server *kdc, 
                                     TALLOC_CTX *mem_ctx, 
                                     uint16_t result_code, 
                                     const char *error_string, 
                                     DATA_BLOB *error_blob) 
 {
        char *error_string_utf8;
-       ssize_t len;
+       size_t len;
        
        DEBUG(result_code ? 3 : 10, ("kpasswdd: %s\n", error_string));
 
-       len = push_utf8_talloc(mem_ctx, &error_string_utf8, error_string);
-       if (len == -1) {
-               return False;
+       if (!push_utf8_talloc(mem_ctx, &error_string_utf8, error_string, &len)) {
+               return false;
        }
 
        *error_blob = data_blob_talloc(mem_ctx, NULL, 2 + len + 1);
        if (!error_blob->data) {
-               return False;
+               return false;
        }
        RSSVAL(error_blob->data, 0, result_code);
        memcpy(error_blob->data + 2, error_string_utf8, len + 1);
-       return True;
+       return true;
 }
 
 /* Return true if there is a valid error packet formed in the error_blob */
-static BOOL kpasswdd_make_unauth_error_reply(struct kdc_server *kdc, 
+static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc, 
                                            TALLOC_CTX *mem_ctx, 
                                            uint16_t result_code, 
                                            const char *error_string, 
                                            DATA_BLOB *error_blob) 
 {
-       BOOL ret;
+       bool ret;
        int kret;
        DATA_BLOB error_bytes;
        krb5_data k5_error_bytes, k5_error_blob;
        ret = kpasswdd_make_error_reply(kdc, mem_ctx, result_code, error_string, 
                                       &error_bytes);
        if (!ret) {
-               return False;
+               return false;
        }
        k5_error_bytes.data = error_bytes.data;
        k5_error_bytes.length = error_bytes.length;
@@ -98,20 +91,20 @@ static BOOL kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
                             result_code, NULL, &k5_error_bytes, 
                             NULL, NULL, NULL, NULL, &k5_error_blob);
        if (kret) {
-               return False;
+               return false;
        }
        *error_blob = data_blob_talloc(mem_ctx, k5_error_blob.data, k5_error_blob.length);
        krb5_data_free(&k5_error_blob);
        if (!error_blob->data) {
-               return False;
+               return false;
        }
-       return True;
+       return true;
 }
 
-static BOOL kpasswd_make_pwchange_reply(struct kdc_server *kdc, 
+static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc, 
                                        TALLOC_CTX *mem_ctx, 
                                        NTSTATUS status, 
-                                       enum samr_RejectReason reject_reason,
+                                       enum samPwdChangeReason reject_reason,
                                        struct samr_DomInfo1 *dominfo,
                                        DATA_BLOB *error_blob) 
 {
@@ -130,17 +123,16 @@ static BOOL kpasswd_make_pwchange_reply(struct kdc_server *kdc,
        if (dominfo && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
                const char *reject_string;
                switch (reject_reason) {
-               case SAMR_REJECT_TOO_SHORT:
+               case SAM_PWD_CHANGE_PASSWORD_TOO_SHORT:
                        reject_string = talloc_asprintf(mem_ctx, "Password too short, password must be at least %d characters long",
                                                        dominfo->min_password_length);
                        break;
-               case SAMR_REJECT_COMPLEXITY:
+               case SAM_PWD_CHANGE_NOT_COMPLEX:
                        reject_string = "Password does not meet complexity requirements";
                        break;
-               case SAMR_REJECT_IN_HISTORY:
+               case SAM_PWD_CHANGE_PWD_IN_HISTORY:
                        reject_string = "Password is already in password history";
                        break;
-               case SAMR_REJECT_OTHER:
                default:
                        reject_string = talloc_asprintf(mem_ctx, "Password must be at least %d characters long, and cannot match any of your %d previous passwords",
                                                        dominfo->min_password_length, dominfo->password_history_length);
@@ -169,18 +161,18 @@ static BOOL kpasswd_make_pwchange_reply(struct kdc_server *kdc,
    Return true if there is a valid error packet (or sucess) formed in
    the error_blob
 */
-static BOOL kpasswdd_change_password(struct kdc_server *kdc,
+static bool kpasswdd_change_password(struct kdc_server *kdc,
                                     TALLOC_CTX *mem_ctx, 
                                     struct auth_session_info *session_info,
-                                    const char *password,
+                                    const DATA_BLOB *password,
                                     DATA_BLOB *reply)
 {
        NTSTATUS status;
-       enum samr_RejectReason reject_reason;
+       enum samPwdChangeReason reject_reason;
        struct samr_DomInfo1 *dominfo;
        struct ldb_context *samdb;
 
-       samdb = samdb_connect(mem_ctx, system_session(mem_ctx));
+       samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(kdc->task->lp_ctx));
        if (!samdb) {
                return kpasswdd_make_error_reply(kdc, mem_ctx, 
                                                KRB5_KPASSWD_HARDERROR,
@@ -197,8 +189,7 @@ static BOOL kpasswdd_change_password(struct kdc_server *kdc,
        status = samdb_set_password_sid(samdb, mem_ctx, 
                                        session_info->security_token->user_sid,
                                        password, NULL, NULL, 
-                                       True, /* this is a user password change */
-                                       True, /* run restriction tests */
+                                       true, /* this is a user password change */
                                        &reject_reason,
                                        &dominfo);
        return kpasswd_make_pwchange_reply(kdc, mem_ctx, 
@@ -209,7 +200,7 @@ static BOOL kpasswdd_change_password(struct kdc_server *kdc,
 
 }
 
-static BOOL kpasswd_process_request(struct kdc_server *kdc,
+static bool kpasswd_process_request(struct kdc_server *kdc,
                                    TALLOC_CTX *mem_ctx, 
                                    struct gensec_security *gensec_security,
                                    uint16_t version,
@@ -217,6 +208,8 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                                    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, 
@@ -228,25 +221,31 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
        switch (version) {
        case KRB5_KPASSWD_VERS_CHANGEPW:
        {
-               char *password = talloc_strndup(mem_ctx, (const char *)input->data, input->length);
-               if (!password) {
-                       return False;
+               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);
+                                               &password, reply);
                break;
        }
        case KRB5_KPASSWD_VERS_SETPW:
        {
                NTSTATUS status;
-               enum samr_RejectReason reject_reason = SAMR_REJECT_OTHER;
+               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;
-               char *password;
+               DATA_BLOB password;
 
                krb5_principal principal;
                char *set_password_on_princ;
@@ -257,7 +256,7 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
 
                msg = ldb_msg_new(mem_ctx);
                if (!msg) {
-                       return False;
+                       return false;
                }
 
                ret = decode_ChangePasswdDataMS(input->data, input->length,
@@ -269,12 +268,17 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                                                        reply);
                }
                
-               password = talloc_strndup(mem_ctx, chpw.newpasswd.data, 
-                                         chpw.newpasswd.length);
-               if (!password) {
+               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;
+                       return false;
                }
+               
+               password.length = pw_len;
+       
                if ((chpw.targname && !chpw.targrealm) 
                    || (!chpw.targname && chpw.targrealm)) {
                        return kpasswdd_make_error_reply(kdc, mem_ctx, 
@@ -283,6 +287,7 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                                                        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) {
@@ -293,10 +298,16 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                                                                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);
+                                                       &password, reply);
                }
                free_ChangePasswdDataMS(&chpw);
 
@@ -310,7 +321,7 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                
                krb5_free_principal(context, principal);
                
-               samdb = samdb_connect(mem_ctx, session_info);
+               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,
@@ -328,7 +339,7 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                        status = NT_STATUS_TRANSACTION_ABORTED;
                        return kpasswd_make_pwchange_reply(kdc, mem_ctx, 
                                                           status,
-                                                          SAMR_REJECT_OTHER, 
+                                                          SAM_PWD_CHANGE_NO_ERROR,
                                                           NULL, 
                                                           reply);
                }
@@ -341,7 +352,7 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                        ldb_transaction_cancel(samdb);
                        return kpasswd_make_pwchange_reply(kdc, mem_ctx, 
                                                           status,
-                                                          SAMR_REJECT_OTHER, 
+                                                          SAM_PWD_CHANGE_NO_ERROR,
                                                           NULL, 
                                                           reply);
                }
@@ -361,9 +372,8 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                        /* 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 */
-                                                   True, /* run restriction tests */
+                                                   msg, &password, NULL, NULL, 
+                                                   false, /* this is not a user password change */
                                                    &reject_reason, &dominfo);
                }
 
@@ -402,18 +412,18 @@ static BOOL kpasswd_process_request(struct kdc_server *kdc,
                                                                 version),
                                                 reply);
        }
-       return True;
+       return true;
 }
 
-BOOL kpasswdd_process(struct kdc_server *kdc,
+bool kpasswdd_process(struct kdc_server *kdc,
                      TALLOC_CTX *mem_ctx, 
                      DATA_BLOB *input, 
                      DATA_BLOB *reply,
-                     struct socket_address *peer_addr,
-                     struct socket_address *my_addr,
+                     struct tsocket_address *peer_addr,
+                     struct tsocket_address *my_addr,
                      int datagram_reply)
 {
-       BOOL ret;
+       bool ret;
        const uint16_t header_len = 6;
        uint16_t len;
        uint16_t ap_req_len;
@@ -426,23 +436,28 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
        DATA_BLOB kpasswd_req, kpasswd_rep;
        struct cli_credentials *server_credentials;
        struct gensec_security *gensec_security;
+       struct sockaddr_storage ss;
+       ssize_t socklen;
+       struct socket_address *socket_address;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
-       
+
+       char *keytab_name;
+
        if (!tmp_ctx) {
-               return False;
+               return false;
        }
 
        /* Be parinoid.  We need to ensure we don't just let the
         * caller lead us into a buffer overflow */
        if (input->length <= header_len) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
 
        len = RSVAL(input->data, 0);
        if (input->length != len) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* There are two different versions of this protocol so far,
@@ -452,31 +467,29 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
        ap_req_len = RSVAL(input->data, 4);
        if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
        
        krb_priv_len = len - ap_req_len;
        ap_req = data_blob_const(&input->data[header_len], ap_req_len);
        krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);
        
-       nt_status = gensec_server_start(tmp_ctx, kdc->task->event_ctx, kdc->task->msg_ctx, &gensec_security);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               talloc_free(tmp_ctx);
-               return False;
-       }
-
        server_credentials = cli_credentials_init(tmp_ctx);
        if (!server_credentials) {
                DEBUG(1, ("Failed to init server credentials\n"));
-               return False;
+               return false;
        }
 
        /* We want the credentials subsystem to use the krb5 context
         * we already have, rather than a new context */        
        cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
-       cli_credentials_set_conf(server_credentials);
-       nt_status = cli_credentials_set_stored_principal(server_credentials, "kadmin/changepw");
-       if (!NT_STATUS_IS_OK(nt_status)) {
+       cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
+
+       keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->hdb_samba4_context);
+
+       cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED);
+       ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED);
+       if (ret != 0) {
                ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, 
                                                       KRB5_KPASSWD_HARDERROR,
                                                       talloc_asprintf(mem_ctx, 
@@ -491,23 +504,53 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
                return ret;
        }
        
-       nt_status = gensec_set_credentials(gensec_security, server_credentials);
+       /* We don't strictly need to call this wrapper, and could call
+        * gensec_server_start directly, as we have no need for NTLM
+        * and we have a PAC, but this ensures that the wrapper can be
+        * safely extended for other helpful things in future */
+       nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx, 
+                                             kdc->task->msg_ctx,
+                                             kdc->task->lp_ctx,
+                                             server_credentials,
+                                             "kpasswd", 
+                                             &gensec_security);
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* The kerberos PRIV packets include these addresses.  MIT
         * clients check that they are present */
+#if 0
+       /* Skip this part for now, it breaks with a NetAPP filer and
+        * in any case where the client address is behind NAT.  If
+        * older MIT clients need this, we might have to insert more
+        * complex code */
+
        nt_status = gensec_set_peer_addr(gensec_security, peer_addr);
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
-       nt_status = gensec_set_my_addr(gensec_security, my_addr);
+#endif
+
+       socklen = tsocket_address_bsd_sockaddr(my_addr, (struct sockaddr *) &ss,
+                               sizeof(struct sockaddr_storage));
+       if (socklen < 0) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
+       socket_address = socket_address_from_sockaddr(tmp_ctx,
+                       (struct sockaddr *) &ss, socklen);
+       if (socket_address == NULL) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
+
+       nt_status = gensec_set_my_addr(gensec_security, socket_address);
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* We want the GENSEC wrap calls to generate PRIV tokens */
@@ -516,7 +559,7 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
        nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
        if (!NT_STATUS_IS_OK(nt_status)) {
                talloc_free(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* Accept the AP-REQ and generate teh AP-REP we need for the reply */
@@ -561,7 +604,7 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
                                      &kpasswd_req, &kpasswd_rep); 
        if (!ret) {
                /* Argh! */
-               return False;
+               return false;
        }
 
        /* And wrap up the reply: This ensures that the error message
@@ -586,7 +629,7 @@ BOOL kpasswdd_process(struct kdc_server *kdc,
 reply:
        *reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
        if (!reply->data) {
-               return False;
+               return false;
        }
 
        RSSVAL(reply->data, 0, reply->length);