r11137: Compile with only 2 warnings (I'm still working on that code) on a gcc4
[abartlet/samba.git/.git] / source3 / libads / krb5_setpw.c
index c3ec754e393fef0126f8927316a10004f63c5593..5488c5908f8fe8dff7c8bf11d35e0ed0e1f6dbde 100644 (file)
@@ -25,8 +25,8 @@
 
 #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_ACCESSDENIED              5
 #define KRB5_KPASSWD_BAD_VERSION               6
 #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED       7
@@ -54,9 +54,9 @@ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
        DATA_BLOB ret;
 
 
-       princ = strdup(principal);
+       princ = SMB_STRDUP(principal);
 
-       if ((c = strchr(princ, '/')) == NULL) {
+       if ((c = strchr_m(princ, '/')) == NULL) {
            c = princ; 
        } else {
            *c = '\0';
@@ -66,7 +66,7 @@ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
 
        princ_part2 = c;
 
-       if ((c = strchr(c, '@')) != NULL) {
+       if ((c = strchr_m(c, '@')) != NULL) {
            *c = '\0';
            c++;
            realm = c;
@@ -138,12 +138,12 @@ 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;
+       encoded_setpw.data = (char *)setpw.data;
        encoded_setpw.length = setpw.length;
 
        ret = krb5_mk_priv(context, auth_context,
@@ -156,7 +156,7 @@ 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 + 6);
        if (!packet->data)
                return -1;
 
@@ -178,47 +178,39 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
        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, 
@@ -258,7 +250,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;
@@ -318,29 +310,24 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
        if(res_code == KRB5_KPASSWD_SUCCESS)
                        return 0;
        else {
-               char *errstr;
-               krb5_setpw_result_code_string(context, res_code, &errstr);
+               const char *errstr;
+               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 KV5M_ALT_METHOD;
-                               break;
+                               return KRB5KDC_ERR_BADOPTION;
+                               /* return KV5M_ALT_METHOD; MIT-only define */
                        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;
                }
        }
 }
@@ -354,7 +341,8 @@ 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;
+       int ret, sock;
+       socklen_t addr_len;
        struct sockaddr remote_addr, local_addr;
        krb5_address local_kaddr, remote_kaddr;
 
@@ -414,7 +402,7 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
        free(chpw_req.data);
 
        chpw_rep.length = 1500;
-       chpw_rep.data = (char *) malloc(chpw_rep.length);
+       chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length);
        if (!chpw_rep.data) {
                close(sock);
                free(ap_req.data);
@@ -464,19 +452,26 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
        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);
+       
        ret = krb5_init_context(&context);
        if (ret) {
                DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
@@ -494,14 +489,19 @@ 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 (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);
@@ -511,16 +511,23 @@ ADS_STATUS krb5_set_password(const char *kdc_host, const char *princ, const char
        /* parse the principal we got as a function argument */
        ret = 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)));
                return ADS_ERROR_KRB5(ret);
        }
 
-       krb5_princ_set_realm(context, creds.server,
-                            krb5_princ_realm(context, principal));
+       /* 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", 
@@ -530,7 +537,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)));
@@ -540,13 +550,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;
@@ -578,15 +590,15 @@ 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;
@@ -614,7 +626,7 @@ ADS_STATUS krb5_chg_password(const char *kdc_host,
     /* 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);
+    password = SMB_STRDUP(oldpw);
     ret = krb5_get_init_creds_password(context, &creds, princ, password,
                                           kerb_prompter, NULL, 
                                           0, chpw_princ, &opts);
@@ -650,17 +662,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, 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);
 }
 
 
@@ -672,24 +684,22 @@ 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);
+       asprintf(&principal, "%s@%s", machine_account, ads->config.realm);
        
-       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);
 
        return status;