s3: Remove some pointless uses of string_sid_talloc
[ira/wip.git] / source3 / libads / krb5_setpw.c
index 9d8fb8d24cb9f807fac97896bb488da46a621341..ec5cafc49d408a573c0a08cd07669b35799b3dc6 100644 (file)
@@ -6,7 +6,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 "smb_krb5.h"
 
 #ifdef HAVE_KRB5
 
 #define DEFAULT_KPASSWD_PORT   464
+
 #define KRB5_KPASSWD_VERS_CHANGEPW             1
-#define KRB5_KPASSWD_VERS_SETPW                        2
-#define KRB5_KPASSWD_VERS_SETPW_MS             0xff80
+
+#define KRB5_KPASSWD_VERS_SETPW                        0xff80
+#define KRB5_KPASSWD_VERS_SETPW_ALT            2
+
+#define KRB5_KPASSWD_SUCCESS                   0
+#define KRB5_KPASSWD_MALFORMED                 1
+#define KRB5_KPASSWD_HARDERROR                 2
+#define KRB5_KPASSWD_AUTHERROR                 3
+#define KRB5_KPASSWD_SOFTERROR                 4
 #define KRB5_KPASSWD_ACCESSDENIED              5
 #define KRB5_KPASSWD_BAD_VERSION               6
 #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED       7
 #define KRB5_KPASSWD_BAD_PRINCIPAL             9
 #define KRB5_KPASSWD_ETYPE_NOSUPP              10
 
+/*
+ * we've got to be able to distinguish KRB_ERRORs from other
+ * requests - valid response for CHPW v2 replies.
+ */
+
+#define krb5_is_krb_error(packet) \
+       ( packet && packet->length && (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e))
+
 /* This implements kerberos password change protocol as specified in 
  * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
  * as well as microsoft version of the protocol 
@@ -50,61 +66,68 @@ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
        char* c;
        char* princ;
 
-       ASN1_DATA req;
+       ASN1_DATA *req;
        DATA_BLOB ret;
 
 
-       princ = strdup(principal);
+       princ = SMB_STRDUP(principal);
 
-       if ((c = strchr(princ, '/')) == NULL) {
-           c = princ; 
+       if ((c = strchr_m(princ, '/')) == NULL) {
+               c = princ; 
        } else {
-           *c = '\0';
-           c++;
-           princ_part1 = princ;
+               *c = '\0';
+               c++;
+               princ_part1 = princ;
        }
 
        princ_part2 = c;
 
-       if ((c = strchr(c, '@')) != NULL) {
-           *c = '\0';
-           c++;
-           realm = c;
+       if ((c = strchr_m(c, '@')) != NULL) {
+               *c = '\0';
+               c++;
+               realm = c;
+       } else {
+               /* We must have a realm component. */
+               return data_blob_null;
+       }
+
+       req = asn1_init(talloc_tos());
+       if (req == NULL) {
+               return data_blob_null;
        }
 
-       memset(&req, 0, sizeof(req));
-       
-       asn1_push_tag(&req, ASN1_SEQUENCE(0));
-       asn1_push_tag(&req, ASN1_CONTEXT(0));
-       asn1_write_OctetString(&req, password, strlen(password));
-       asn1_pop_tag(&req);
+       asn1_push_tag(req, ASN1_SEQUENCE(0));
+       asn1_push_tag(req, ASN1_CONTEXT(0));
+       asn1_write_OctetString(req, password, strlen(password));
+       asn1_pop_tag(req);
 
-       asn1_push_tag(&req, ASN1_CONTEXT(1));
-       asn1_push_tag(&req, ASN1_SEQUENCE(0));
+       asn1_push_tag(req, ASN1_CONTEXT(1));
+       asn1_push_tag(req, ASN1_SEQUENCE(0));
 
-       asn1_push_tag(&req, ASN1_CONTEXT(0));
-       asn1_write_Integer(&req, 1);
-       asn1_pop_tag(&req);
+       asn1_push_tag(req, ASN1_CONTEXT(0));
+       asn1_write_Integer(req, 1);
+       asn1_pop_tag(req);
 
-       asn1_push_tag(&req, ASN1_CONTEXT(1));
-       asn1_push_tag(&req, ASN1_SEQUENCE(0));
+       asn1_push_tag(req, ASN1_CONTEXT(1));
+       asn1_push_tag(req, ASN1_SEQUENCE(0));
 
-       if (princ_part1) 
-           asn1_write_GeneralString(&req, princ_part1);
+       if (princ_part1) {
+               asn1_write_GeneralString(req, princ_part1);
+       }
        
-       asn1_write_GeneralString(&req, princ_part2);
-       asn1_pop_tag(&req);
-       asn1_pop_tag(&req);
-       asn1_pop_tag(&req);
-       asn1_pop_tag(&req);
+       asn1_write_GeneralString(req, princ_part2);
+       asn1_pop_tag(req);
+       asn1_pop_tag(req);
+       asn1_pop_tag(req);
+       asn1_pop_tag(req);
 
-       asn1_push_tag(&req, ASN1_CONTEXT(2));
-       asn1_write_GeneralString(&req, realm);
-       asn1_pop_tag(&req);
-       asn1_pop_tag(&req);
+       asn1_push_tag(req, ASN1_CONTEXT(2));
+       asn1_write_GeneralString(req, realm);
+       asn1_pop_tag(req);
+       asn1_pop_tag(req);
 
-       ret = data_blob(req.data, req.length);
-       asn1_free(&req);
+       ret = data_blob(req->data, req->length);
+       asn1_free(req);
 
        free(princ);
 
@@ -117,14 +140,16 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
                                           krb5_data *ap_req,
                                           const char *princ,
                                           const char *passwd,
+                                          bool use_tcp,
                                           krb5_data *packet)
 {
        krb5_error_code ret;
        krb5_data cipherpw;
        krb5_data encoded_setpw;
        krb5_replay_data replay;
-       char *p;
+       char *p, *msg_start;
        DATA_BLOB setpw;
+       unsigned int msg_length;
 
        ret = krb5_auth_con_setflags(context,
                                     auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
@@ -138,12 +163,16 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
        if (pversion  == KRB5_KPASSWD_VERS_CHANGEPW)
                setpw = data_blob(passwd, strlen(passwd));
        else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
-                pversion == KRB5_KPASSWD_VERS_SETPW_MS)
+                pversion == KRB5_KPASSWD_VERS_SETPW_ALT)
                setpw = encode_krb5_setpw(princ, passwd);
        else
                return EINVAL;
 
-       encoded_setpw.data = setpw.data;
+       if (setpw.data == NULL || setpw.length == 0) {
+               return EINVAL;
+       }
+
+       encoded_setpw.data = (char *)setpw.data;
        encoded_setpw.length = setpw.length;
 
        ret = krb5_mk_priv(context, auth_context,
@@ -156,12 +185,16 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
                return ret;
        }
 
-       packet->data = (char *)malloc(ap_req->length + cipherpw.length + 6);
+       packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 ));
        if (!packet->data)
                return -1;
 
+
+
        /* see the RFC for details */
-       p = ((char *)packet->data) + 2;
+
+       msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0);
+       p += 2;
        RSSVAL(p, 0, pversion);
        p += 2;
        RSSVAL(p, 0, ap_req->length);
@@ -171,57 +204,74 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
        memcpy(p, cipherpw.data, cipherpw.length);
        p += cipherpw.length;
        packet->length = PTR_DIFF(p,packet->data);
-       RSSVAL(packet->data, 0, packet->length);
+       msg_length = PTR_DIFF(p,msg_start);
+
+       if (use_tcp) {
+               RSIVAL(packet->data, 0, msg_length);
+       }
+       RSSVAL(msg_start, 0, msg_length);
        
        free(cipherpw.data);    /* from 'krb5_mk_priv(...)' */
 
        return 0;
 }
 
-static krb5_error_code krb5_setpw_result_code_string(krb5_context context,
-                                                    int result_code,
-                                                    char **code_string)
+static const struct kpasswd_errors {
+       int result_code;
+       const char *error_string;
+} kpasswd_errors[] = {
+       {KRB5_KPASSWD_MALFORMED, "Malformed request error"},
+       {KRB5_KPASSWD_HARDERROR, "Server error"},
+       {KRB5_KPASSWD_AUTHERROR, "Authentication error"},
+       {KRB5_KPASSWD_SOFTERROR, "Password change rejected"},
+       {KRB5_KPASSWD_ACCESSDENIED, "Client does not have proper authorization"},
+       {KRB5_KPASSWD_BAD_VERSION, "Protocol version not supported"},
+       {KRB5_KPASSWD_INITIAL_FLAG_NEEDED, "Authorization ticket must have initial flag set"},
+       {KRB5_KPASSWD_POLICY_REJECT, "Password rejected due to policy requirements"},
+       {KRB5_KPASSWD_BAD_PRINCIPAL, "Target principal does not exist"},
+       {KRB5_KPASSWD_ETYPE_NOSUPP, "Unsupported encryption type"},
+       {0, NULL}
+};
+
+static krb5_error_code setpw_result_code_string(krb5_context context,
+                                               int result_code,
+                                               const char **code_string)
 {
-   switch (result_code) {
-   case KRB5_KPASSWD_MALFORMED:
-      *code_string = "Malformed request error";
-      break;
-   case KRB5_KPASSWD_HARDERROR:
-      *code_string = "Server error";
-      break;
-   case KRB5_KPASSWD_AUTHERROR:
-      *code_string = "Authentication error";
-      break;
-   case KRB5_KPASSWD_SOFTERROR:
-      *code_string = "Password change rejected";
-      break;
-   case KRB5_KPASSWD_ACCESSDENIED:
-      *code_string = "Client does not have proper authorization";
-      break;
-   case KRB5_KPASSWD_BAD_VERSION:
-      *code_string = "Protocol version not supported";
-      break;
-   case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
-      *code_string = "Authorization ticket must have initial flag set";
-      break;
-   case KRB5_KPASSWD_POLICY_REJECT:
-      *code_string = "Password rejected due to policy requirements";
-      break;
-   case KRB5_KPASSWD_BAD_PRINCIPAL:
-      *code_string = "Target principal does not exist";
-      break;
-   case KRB5_KPASSWD_ETYPE_NOSUPP:
-      *code_string = "Unsupported encryption type";
-      break;
-   default:
-      *code_string = "Password change failed";
-      break;
-   }
-
-   return(0);
+        unsigned int idx = 0;
+
+       while (kpasswd_errors[idx].error_string != NULL) {
+               if (kpasswd_errors[idx].result_code == 
+                    result_code) {
+                       *code_string = kpasswd_errors[idx].error_string;
+                       return 0;
+               }
+               idx++;
+       }
+       *code_string = "Password change failed";
+        return (0);
 }
 
-static krb5_error_code parse_setpw_reply(krb5_context context, 
+ krb5_error_code kpasswd_err_to_krb5_err(krb5_error_code res_code) 
+{
+       switch(res_code) {
+               case KRB5_KPASSWD_ACCESSDENIED:
+                       return KRB5KDC_ERR_BADOPTION;
+               case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
+                       return KRB5KDC_ERR_BADOPTION;
+                       /* return KV5M_ALT_METHOD; MIT-only define */
+               case KRB5_KPASSWD_ETYPE_NOSUPP:
+                       return KRB5KDC_ERR_ETYPE_NOSUPP;
+               case KRB5_KPASSWD_BAD_PRINCIPAL:
+                       return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+               case KRB5_KPASSWD_POLICY_REJECT:
+               case KRB5_KPASSWD_SOFTERROR:
+                       return KRB5KDC_ERR_POLICY;
+               default:
+                       return KRB5KRB_ERR_GENERIC;
+       }
+}
+static krb5_error_code parse_setpw_reply(krb5_context context,
+                                        bool use_tcp,
                                         krb5_auth_context auth_context,
                                         krb5_data *packet)
 {
@@ -232,23 +282,41 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
        krb5_data clearresult;
        krb5_ap_rep_enc_part *ap_rep_enc;
        krb5_replay_data replay;
-       
-       if (packet->length < 4) {
+       unsigned int msg_length = packet->length;
+
+
+       if (packet->length < (use_tcp ? 8 : 4)) {
                return KRB5KRB_AP_ERR_MODIFIED;
        }
        
-       p = packet->data;
+       p = (char *)packet->data;
+       /*
+       ** see if it is an error
+       */
+       if (krb5_is_krb_error(packet)) {
+
+               ret = handle_krberror_packet(context, packet);
+               if (ret) {
+                       return ret;
+               }
+       }
+
        
-       if (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e) {
-               /* it's an error packet. We should parse it ... */
-               DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
-                        ((char *)packet->data)[0]));
-               return KRB5KRB_AP_ERR_MODIFIED;
+       /* tcp... */
+       if (use_tcp) {
+               msg_length -= 4;
+               if (RIVAL(p, 0) != msg_length) {
+                       DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n",
+                       RIVAL(p, 0), msg_length));
+                       return KRB5KRB_AP_ERR_MODIFIED;
+               }
+
+               p += 4;
        }
        
-       if (RSVAL(p, 0) != packet->length) {
+       if (RSVAL(p, 0) != msg_length) {
                DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
-                        RSVAL(p, 0), packet->length));
+                        RSVAL(p, 0), msg_length));
                return KRB5KRB_AP_ERR_MODIFIED;
        }
 
@@ -258,7 +326,7 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
 
        /* FIXME: According to standard there is only one type of reply */      
        if (vnum != KRB5_KPASSWD_VERS_SETPW && 
-           vnum != KRB5_KPASSWD_VERS_SETPW_MS && 
+           vnum != KRB5_KPASSWD_VERS_SETPW_ALT && 
            vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
                DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
                return KRB5KDC_ERR_BAD_PVNO;
@@ -304,7 +372,7 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
                return KRB5KRB_AP_ERR_MODIFIED;
        }
        
-       p = clearresult.data;
+       p = (char *)clearresult.data;
        
        res_code = RSVAL(p, 0);
        
@@ -315,34 +383,14 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
                return KRB5KRB_AP_ERR_MODIFIED;
        }
 
-       if(res_code == KRB5_KPASSWD_SUCCESS)
-                       return 0;
-       else {
-               char *errstr;
-               krb5_setpw_result_code_string(context, res_code, &errstr);
-               DEBUG(1, ("Error changing password: %s\n", errstr));
-
-               switch(res_code) {
-                       case KRB5_KPASSWD_ACCESSDENIED:
-                               return KRB5KDC_ERR_BADOPTION;
-                               break;
-                       case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
-                               return KRB5KDC_ERR_BADOPTION;
-                               /* return KV5M_ALT_METHOD; MIT-only define */
-                               break;
-                       case KRB5_KPASSWD_ETYPE_NOSUPP:
-                               return KRB5KDC_ERR_ETYPE_NOSUPP;
-                               break;
-                       case KRB5_KPASSWD_BAD_PRINCIPAL:
-                               return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
-                               break;
-                       case KRB5_KPASSWD_POLICY_REJECT:
-                               return KRB5KDC_ERR_POLICY;
-                               break;
-                       default:
-                               return KRB5KRB_ERR_GENERIC;
-                               break;
-               }
+       if (res_code == KRB5_KPASSWD_SUCCESS) {
+               return 0;
+       } else {
+               const char *errstr;
+               setpw_result_code_string(context, res_code, &errstr);
+               DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code));
+
+               return kpasswd_err_to_krb5_err(res_code);
        }
 }
 
@@ -355,129 +403,190 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
 {
        krb5_auth_context auth_context = NULL;
        krb5_data ap_req, chpw_req, chpw_rep;
-       int ret, sock, addr_len;
-       struct sockaddr remote_addr, local_addr;
+       int ret, sock;
+       socklen_t addr_len;
+       struct sockaddr_storage remote_addr, local_addr;
+       struct sockaddr_storage addr;
        krb5_address local_kaddr, remote_kaddr;
+       bool use_tcp = False;
 
-       ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
-                                  NULL, credsp, &ap_req);
-       if (ret) {
-               DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-       
-       sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
-       if (sock == -1) {
-               int rc = errno;
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("failed to open kpasswd socket to %s (%s)\n", 
-                        kdc_host, strerror(errno)));
-               return ADS_ERROR_SYSTEM(rc);
-       }
-       
-       addr_len = sizeof(remote_addr);
-       getpeername(sock, &remote_addr, &addr_len);
-       addr_len = sizeof(local_addr);
-       getsockname(sock, &local_addr, &addr_len);
-       
-       setup_kaddr(&remote_kaddr, &remote_addr);
-       setup_kaddr(&local_kaddr, &local_addr);
 
-       ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
-       if (ret) {
-               close(sock);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
+       if (!interpret_string_addr(&addr, kdc_host, 0)) {
        }
 
-       ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
-                                 princ, newpw, &chpw_req);
+       ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
+                                  NULL, credsp, &ap_req);
        if (ret) {
-               close(sock);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+               DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
 
-       if (write(sock, chpw_req.data, chpw_req.length) != chpw_req.length) {
-               close(sock);
-               free(chpw_req.data);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
-               return ADS_ERROR_SYSTEM(errno);
-       }
+       do {
 
-       free(chpw_req.data);
+               if (!use_tcp) {
 
-       chpw_rep.length = 1500;
-       chpw_rep.data = (char *) malloc(chpw_rep.length);
-       if (!chpw_rep.data) {
-               close(sock);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
-               errno = ENOMEM;
-               return ADS_ERROR_SYSTEM(errno);
-       }
+                       sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
+                       if (sock == -1) {
+                               int rc = errno;
+                               SAFE_FREE(ap_req.data);
+                               krb5_auth_con_free(context, auth_context);
+                               DEBUG(1,("failed to open kpasswd socket to %s "
+                                        "(%s)\n", kdc_host, strerror(errno)));
+                               return ADS_ERROR_SYSTEM(rc);
+                       }
+               } else {
+                       NTSTATUS status;
+                       status = open_socket_out(&addr, DEFAULT_KPASSWD_PORT,
+                                                LONG_CONNECT_TIMEOUT, &sock);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               SAFE_FREE(ap_req.data);
+                               krb5_auth_con_free(context, auth_context);
+                               DEBUG(1,("failed to open kpasswd socket to %s "
+                                        "(%s)\n", kdc_host,
+                                        nt_errstr(status)));
+                               return ADS_ERROR_NT(status);
+                       }
+               }
 
-       ret = read(sock, chpw_rep.data, chpw_rep.length);
-       if (ret < 0) {
-               close(sock);
-               free(chpw_rep.data);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
-               return ADS_ERROR_SYSTEM(errno);
-       }
+               addr_len = sizeof(remote_addr);
+               if (getpeername(sock, (struct sockaddr *)&remote_addr, &addr_len) != 0) {
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("getpeername() failed (%s)\n", error_message(errno)));
+                       return ADS_ERROR_SYSTEM(errno);
+               }
+               addr_len = sizeof(local_addr);
+               if (getsockname(sock, (struct sockaddr *)&local_addr, &addr_len) != 0) {
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("getsockname() failed (%s)\n", error_message(errno)));
+                       return ADS_ERROR_SYSTEM(errno);
+               }
+               if (!setup_kaddr(&remote_kaddr, &remote_addr) ||
+                               !setup_kaddr(&local_kaddr, &local_addr)) {
+                       DEBUG(1,("do_krb5_kpasswd_request: "
+                               "Failed to setup addresses.\n"));
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       errno = EINVAL;
+                       return ADS_ERROR_SYSTEM(EINVAL);
+               }
 
-       close(sock);
-       chpw_rep.length = ret;
+               ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
+               if (ret) {
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
+                       return ADS_ERROR_KRB5(ret);
+               }
 
-       ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
-       if (ret) {
-               free(chpw_rep.data);
-               free(ap_req.data);
-               krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", 
-                        error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
+               ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
+                                         princ, newpw, use_tcp, &chpw_req);
+               if (ret) {
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+                       return ADS_ERROR_KRB5(ret);
+               }
 
-       ret = parse_setpw_reply(context, auth_context, &chpw_rep);
-       free(chpw_rep.data);
+               ret = write(sock, chpw_req.data, chpw_req.length); 
 
-       if (ret) {
-               free(ap_req.data);
+               if (ret != chpw_req.length) {
+                       close(sock);
+                       SAFE_FREE(chpw_req.data);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+                       return ADS_ERROR_SYSTEM(errno);
+               }
+       
+               SAFE_FREE(chpw_req.data);
+       
+               chpw_rep.length = 1500;
+               chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length);
+               if (!chpw_rep.data) {
+                       close(sock);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+                       errno = ENOMEM;
+                       return ADS_ERROR_SYSTEM(errno);
+               }
+       
+               ret = read(sock, chpw_rep.data, chpw_rep.length);
+               if (ret < 0) {
+                       close(sock);
+                       SAFE_FREE(chpw_rep.data);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
+                       return ADS_ERROR_SYSTEM(errno);
+               }
+       
+               close(sock);
+               chpw_rep.length = ret;
+       
+               ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
+               if (ret) {
+                       SAFE_FREE(chpw_rep.data);
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", 
+                                error_message(ret)));
+                       return ADS_ERROR_KRB5(ret);
+               }
+       
+               ret = parse_setpw_reply(context, use_tcp, auth_context, &chpw_rep);
+               SAFE_FREE(chpw_rep.data);
+       
+               if (ret) {
+                       
+                       if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
+                               DEBUG(5, ("Trying setpw with TCP!!!\n"));
+                               use_tcp = True;
+                               continue;
+                       }
+
+                       SAFE_FREE(ap_req.data);
+                       krb5_auth_con_free(context, auth_context);
+                       DEBUG(1,("parse_setpw_reply failed (%s)\n", 
+                                error_message(ret)));
+                       return ADS_ERROR_KRB5(ret);
+               }
+       
+               SAFE_FREE(ap_req.data);
                krb5_auth_con_free(context, auth_context);
-               DEBUG(1,("parse_setpw_reply failed (%s)\n", 
-                        error_message(ret)));
-               return ADS_ERROR_KRB5(ret);
-       }
-
-       free(ap_req.data);
-       krb5_auth_con_free(context, auth_context);
+       } while ( ret );
 
        return ADS_SUCCESS;
 }
 
-ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char *newpw
-                            int time_offset)
+ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *princ
+                                const char *newpw, int time_offset)
 {
 
        ADS_STATUS aret;
-       krb5_error_code ret;
-       krb5_context context;
-       krb5_principal principal;
-       char *princ_name;
-       char *realm;
-       krb5_creds creds, *credsp;
-       krb5_ccache ccache;
+       krb5_error_code ret = 0;
+       krb5_context context = NULL;
+       krb5_principal principal = NULL;
+       char *princ_name = NULL;
+       char *realm = NULL;
+       krb5_creds creds, *credsp = NULL;
+#if KRB5_PRINC_REALM_RETURNS_REALM
+       krb5_realm orig_realm;
+#else
+       krb5_data orig_realm;
+#endif
+       krb5_ccache ccache = NULL;
 
+       ZERO_STRUCT(creds);
+       
+       initialize_krb5_error_table();
        ret = krb5_init_context(&context);
        if (ret) {
                DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
@@ -495,33 +604,53 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char
                return ADS_ERROR_KRB5(ret);
        }
 
-       ZERO_STRUCT(creds);
-       
-       realm = strchr(princ, '@');
+       realm = strchr_m(princ, '@');
+       if (!realm) {
+               krb5_cc_close(context, ccache);
+               krb5_free_context(context);
+               DEBUG(1,("Failed to get realm\n"));
+               return ADS_ERROR_KRB5(-1);
+       }
        realm++;
 
-       asprintf(&princ_name, "kadmin/changepw@%s", realm);
-       ret = krb5_parse_name(context, princ_name, &creds.server);
+       if (asprintf(&princ_name, "kadmin/changepw@%s", realm) == -1) {
+               krb5_cc_close(context, ccache);
+                krb5_free_context(context);
+               DEBUG(1,("asprintf failed\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       ret = smb_krb5_parse_name(context, princ_name, &creds.server);
        if (ret) {
+               krb5_cc_close(context, ccache);
                 krb5_free_context(context);
                DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret)));
                return ADS_ERROR_KRB5(ret);
        }
-       free(princ_name);
 
        /* parse the principal we got as a function argument */
-       ret = krb5_parse_name(context, princ, &principal);
+       ret = smb_krb5_parse_name(context, princ, &principal);
        if (ret) {
+               krb5_cc_close(context, ccache);
+               krb5_free_principal(context, creds.server);
                 krb5_free_context(context);
                DEBUG(1,("Failed to parse %s (%s)\n", princ_name, error_message(ret)));
+               free(princ_name);
                return ADS_ERROR_KRB5(ret);
        }
 
-       krb5_princ_set_realm(context, creds.server,
-                            krb5_princ_realm(context, principal));
+       free(princ_name);
+
+       /* The creds.server principal takes ownership of this memory.
+               Remember to set back to original value before freeing. */
+       orig_realm = *krb5_princ_realm(context, creds.server);
+       krb5_princ_set_realm(context, creds.server, krb5_princ_realm(context, principal));
        
        ret = krb5_cc_get_principal(context, ccache, &creds.client);
        if (ret) {
+               krb5_cc_close(context, ccache);
+               krb5_princ_set_realm(context, creds.server, &orig_realm);
+               krb5_free_principal(context, creds.server);
                krb5_free_principal(context, principal);
                 krb5_free_context(context);
                DEBUG(1,("Failed to get principal from ccache (%s)\n", 
@@ -531,7 +660,10 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char
        
        ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); 
        if (ret) {
+               krb5_cc_close(context, ccache);
                krb5_free_principal(context, creds.client);
+               krb5_princ_set_realm(context, creds.server, &orig_realm);
+               krb5_free_principal(context, creds.server);
                krb5_free_principal(context, principal);
                krb5_free_context(context);
                DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
@@ -541,13 +673,15 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char
        /* we might have to call krb5_free_creds(...) from now on ... */
 
        aret = do_krb5_kpasswd_request(context, kdc_host,
-                                      KRB5_KPASSWD_VERS_SETPW_MS,
+                                      KRB5_KPASSWD_VERS_SETPW,
                                       credsp, princ, newpw);
 
        krb5_free_creds(context, credsp);
        krb5_free_principal(context, creds.client);
-       krb5_free_principal(context, creds.server);
+       krb5_princ_set_realm(context, creds.server, &orig_realm);
+        krb5_free_principal(context, creds.server);
        krb5_free_principal(context, principal);
+       krb5_cc_close(context, ccache);
        krb5_free_context(context);
 
        return aret;
@@ -570,8 +704,10 @@ kerb_prompter(krb5_context ctx, void *data,
        memset(prompts[0].reply->data, 0, prompts[0].reply->length);
        if (prompts[0].reply->length > 0) {
                if (data) {
-                       strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
-                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+                       strncpy((char *)prompts[0].reply->data,
+                               (const char *)data,
+                               prompts[0].reply->length-1);
+                       prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
                } else {
                        prompts[0].reply->length = 0;
                }
@@ -579,27 +715,28 @@ kerb_prompter(krb5_context ctx, void *data,
        return 0;
 }
 
-ADS_STATUS krb5_chg_password(const char *kdc_host,
-                               const char *principal,
-                               const char *oldpw, 
-                               const char *newpw, 
-                               int time_offset)
+static ADS_STATUS ads_krb5_chg_password(const char *kdc_host,
+                                       const char *principal,
+                                       const char *oldpw, 
+                                       const char *newpw, 
+                                       int time_offset)
 {
     ADS_STATUS aret;
     krb5_error_code ret;
-    krb5_context context;
+    krb5_context context = NULL;
     krb5_principal princ;
     krb5_get_init_creds_opt opts;
     krb5_creds creds;
     char *chpw_princ = NULL, *password;
 
+    initialize_krb5_error_table();
     ret = krb5_init_context(&context);
     if (ret) {
        DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
        return ADS_ERROR_KRB5(ret);
     }
 
-    if ((ret = krb5_parse_name(context, principal,
+    if ((ret = smb_krb5_parse_name(context, principal,
                                     &princ))) {
        krb5_free_context(context);
        DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
@@ -613,9 +750,14 @@ ADS_STATUS krb5_chg_password(const char *kdc_host,
     krb5_get_init_creds_opt_set_proxiable(&opts, 0);
 
     /* We have to obtain an INITIAL changepw ticket for changing password */
-    asprintf(&chpw_princ, "kadmin/changepw@%s",
-                               (char *) krb5_princ_realm(context, princ));
-    password = strdup(oldpw);
+    if (asprintf(&chpw_princ, "kadmin/changepw@%s",
+                               (char *) krb5_princ_realm(context, princ)) == -1) {
+       krb5_free_context(context);
+       DEBUG(1,("ads_krb5_chg_password: asprintf fail\n"));
+       return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+    }
+
+    password = SMB_STRDUP(oldpw);
     ret = krb5_get_init_creds_password(context, &creds, princ, password,
                                           kerb_prompter, NULL, 
                                           0, chpw_princ, &opts);
@@ -651,17 +793,17 @@ ADS_STATUS kerberos_set_password(const char *kpasswd_server,
 {
     int ret;
 
-    if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset))) {
+    if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset, NULL))) {
        DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
        return ADS_ERROR_KRB5(ret);
     }
 
     if (!strcmp(auth_principal, target_principal))
-       return krb5_chg_password(kpasswd_server, target_principal,
-                                   auth_password, new_password, time_offset);
+       return ads_krb5_chg_password(kpasswd_server, target_principal,
+                                    auth_password, new_password, time_offset);
     else
-       return krb5_set_password(kpasswd_server, target_principal,
-                                new_password, time_offset);
+       return ads_krb5_set_password(kpasswd_server, target_principal,
+                                    new_password, time_offset);
 }
 
 
@@ -673,29 +815,25 @@ ADS_STATUS kerberos_set_password(const char *kpasswd_server,
  * @return status of password change
  **/
 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
-                                   const char *hostname, 
+                                   const char *machine_account,
                                    const char *password)
 {
        ADS_STATUS status;
-       char *host = strdup(hostname);
-       char *principal; 
-
-       strlower(host);
+       char *principal = NULL; 
 
        /*
-         we need to use the '$' form of the name here, as otherwise the
-         server might end up setting the password for a user instead
+         we need to use the '$' form of the name here (the machine account name), 
+         as otherwise the server might end up setting the password for a user
+         instead
         */
-       asprintf(&principal, "%s$@%s", host, ads->auth.realm);
+       if (asprintf(&principal, "%s@%s", machine_account, ads->config.realm) < 0) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        
-       status = krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset);
+       status = ads_krb5_set_password(ads->auth.kdc_server, principal, 
+                                      password, ads->auth.time_offset);
        
-       free(host);
-       free(principal);
-
+       SAFE_FREE(principal);
        return status;
 }
-
-
-
 #endif