CVE-2016-2110: libcli/auth: pass server_timestamp to SMBNTLMv2encrypt_hash()
[samba.git] / libcli / auth / smbencrypt.c
index a3182cd806bd510de891c86a306e21628fdc89fd..7ad4654f2f7b793c18c378689f76e70a92919df6 100644 (file)
@@ -99,14 +99,10 @@ bool E_md4hash(const char *passwd, uint8_t p16[16])
 
 void E_md5hash(const uint8_t salt[16], const uint8_t nthash[16], uint8_t hash_out[16])
 {
-       struct MD5Context tctx;
-       uint8_t array[32];
-
-       memset(hash_out, '\0', 16);
-       memcpy(array, salt, 16);
-       memcpy(&array[16], nthash, 16);
+       MD5_CTX tctx;
        MD5Init(&tctx);
-       MD5Update(&tctx, array, 32);
+       MD5Update(&tctx, salt, 16);
+       MD5Update(&tctx, nthash, 16);
        MD5Final(hash_out, &tctx);
 }
 
@@ -120,22 +116,39 @@ void E_md5hash(const uint8_t salt[16], const uint8_t nthash[16], uint8_t hash_ou
 
 bool E_deshash(const char *passwd, uint8_t p16[16])
 {
-       bool ret = true;
-       char dospwd[256];
-       ZERO_STRUCT(dospwd);
+       bool ret;
+       uint8_t dospwd[14];
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       /* Password must be converted to DOS charset - null terminated, uppercase. */
-       push_string(dospwd, passwd, sizeof(dospwd), STR_ASCII|STR_UPPER|STR_TERMINATE);
+       size_t converted_size;
 
-       /* Only the first 14 chars are considered, password need not be null terminated. */
-       E_P16((const uint8_t *)dospwd, p16);
+       char *tmpbuf;
+
+       ZERO_STRUCT(dospwd);
 
-       if (strlen(dospwd) > 14) {
-               ret = false;
+       tmpbuf = strupper_talloc(frame, passwd);
+       if (tmpbuf == NULL) {
+               /* Too many callers don't check this result, we need to fill in the buffer with something */
+               strlcpy((char *)dospwd, passwd ? passwd : "", sizeof(dospwd));
+               E_P16(dospwd, p16);
+               talloc_free(frame);
+               return false;
        }
 
        ZERO_STRUCT(dospwd);
 
+       ret = convert_string_error(CH_UNIX, CH_DOS, tmpbuf, strlen(tmpbuf), dospwd, sizeof(dospwd), &converted_size);
+       talloc_free(frame);
+
+       /* Only the first 14 chars are considered, password need not
+        * be null terminated.  We do this in the error and success
+        * case to avoid returning a fixed 'password' buffer, but
+        * callers should not use it when E_deshash returns false */
+
+       E_P16((const uint8_t *)dospwd, p16);
+
+       ZERO_STRUCT(dospwd);
+
        return ret;
 }
 
@@ -172,7 +185,6 @@ void nt_lm_owf_gen(const char *pwd, uint8_t nt_p16[16], uint8_t p16[16])
 /* Does both the NTLMv2 owfs of a user's password */
 bool ntv2_owf_gen(const uint8_t owf[16],
                  const char *user_in, const char *domain_in,
-                 bool upper_case_domain, /* Transform the domain into UPPER case */
                  uint8_t kr_buf[16])
 {
        smb_ucs2_t *user;
@@ -202,14 +214,6 @@ bool ntv2_owf_gen(const uint8_t owf[16],
                return false;
        }
 
-       if (upper_case_domain) {
-               domain_in = strupper_talloc(mem_ctx, domain_in);
-               if (domain_in == NULL) {
-                       talloc_free(mem_ctx);
-                       return false;
-               }
-       }
-
        ret = push_ucs2_talloc(mem_ctx, &user, user_in, &user_byte_len );
        if (!ret) {
                DEBUG(0, ("push_uss2_talloc() for user failed)\n"));
@@ -261,7 +265,7 @@ void SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24])
 
 /* Does the des encryption. */
 
-void SMBNTencrypt_hash(const uint8_t nt_hash[16], uint8_t *c8, uint8_t *p24)
+void SMBNTencrypt_hash(const uint8_t nt_hash[16], const uint8_t *c8, uint8_t *p24)
 {
        uint8_t p21[21];
 
@@ -279,7 +283,7 @@ void SMBNTencrypt_hash(const uint8_t nt_hash[16], uint8_t *c8, uint8_t *p24)
 
 /* Does the NT MD4 hash then des encryption. Plaintext version of the above. */
 
-void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24)
+void SMBNTencrypt(const char *passwd, const uint8_t *c8, uint8_t *p24)
 {
        uint8_t nt_hash[16];
        E_md4hash(passwd, nt_hash);
@@ -367,22 +371,29 @@ DATA_BLOB NTLMv2_generate_names_blob(TALLOC_CTX *mem_ctx,
 {
        DATA_BLOB names_blob = data_blob_talloc(mem_ctx, NULL, 0);
 
-       msrpc_gen(mem_ctx, &names_blob,
-                 "aaa",
-                 MsvAvNbDomainName, domain,
-                 MsvAvNbComputerName, hostname,
-                 MsvAvEOL, "");
+       /* Deliberately ignore return here.. */
+       if (hostname != NULL) {
+               (void)msrpc_gen(mem_ctx, &names_blob,
+                         "aaa",
+                         MsvAvNbDomainName, domain,
+                         MsvAvNbComputerName, hostname,
+                         MsvAvEOL, "");
+       } else {
+               (void)msrpc_gen(mem_ctx, &names_blob,
+                         "aa",
+                         MsvAvNbDomainName, domain,
+                         MsvAvEOL, "");
+       }
        return names_blob;
 }
 
-static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLOB *names_blob)
+static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx,
+                                            NTTIME nttime,
+                                            const DATA_BLOB *names_blob)
 {
        uint8_t client_chal[8];
        DATA_BLOB response = data_blob(NULL, 0);
        uint8_t long_date[8];
-       NTTIME nttime;
-
-       unix_to_nt_time(&nttime, time(NULL));
 
        generate_random_buffer(client_chal, sizeof(client_chal));
 
@@ -390,7 +401,8 @@ static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLO
 
        /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
 
-       msrpc_gen(mem_ctx, &response, "ddbbdb",
+       /* Deliberately ignore return here.. */
+       (void)msrpc_gen(mem_ctx, &response, "ddbbdb",
                  0x00000101,     /* Header  */
                  0,              /* 'Reserved'  */
                  long_date, 8,   /* Timestamp */
@@ -404,6 +416,7 @@ static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLO
 static DATA_BLOB NTLMv2_generate_response(TALLOC_CTX *out_mem_ctx,
                                          const uint8_t ntlm_v2_hash[16],
                                          const DATA_BLOB *server_chal,
+                                         NTTIME nttime,
                                          const DATA_BLOB *names_blob)
 {
        uint8_t ntlmv2_response[16];
@@ -420,7 +433,7 @@ static DATA_BLOB NTLMv2_generate_response(TALLOC_CTX *out_mem_ctx,
        /* NTLMv2 */
        /* generate some data to pass into the response function - including
           the hostname and domain name of the server */
-       ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, names_blob);
+       ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, nttime, names_blob);
 
        /* Given that data, and the challenge from the server, generate a response */
        SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &ntlmv2_client_data, ntlmv2_response);
@@ -466,6 +479,7 @@ static DATA_BLOB LMv2_generate_response(TALLOC_CTX *mem_ctx,
 bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
                           const char *user, const char *domain, const uint8_t nt_hash[16],
                           const DATA_BLOB *server_chal,
+                          const NTTIME *server_timestamp,
                           const DATA_BLOB *names_blob,
                           DATA_BLOB *lm_response, DATA_BLOB *nt_response,
                           DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key)
@@ -476,13 +490,24 @@ bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
           the username and domain.
           This prevents username swapping during the auth exchange
        */
-       if (!ntv2_owf_gen(nt_hash, user, domain, true, ntlm_v2_hash)) {
+       if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) {
                return false;
        }
 
        if (nt_response) {
+               const NTTIME *nttime = server_timestamp;
+               NTTIME _now = 0;
+
+               if (nttime == NULL) {
+                       struct timeval tv_now = timeval_current();
+                       _now = timeval_to_nttime(&tv_now);
+                       nttime = &_now;
+               }
+
                *nt_response = NTLMv2_generate_response(mem_ctx,
-                                                       ntlm_v2_hash, server_chal,
+                                                       ntlm_v2_hash,
+                                                       server_chal,
+                                                       *nttime,
                                                        names_blob);
                if (user_session_key) {
                        *user_session_key = data_blob_talloc(mem_ctx, NULL, 16);
@@ -496,8 +521,13 @@ bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
        /* LMv2 */
 
        if (lm_response) {
-               *lm_response = LMv2_generate_response(mem_ctx,
-                                                     ntlm_v2_hash, server_chal);
+               if (server_timestamp != NULL) {
+                       *lm_response = data_blob_talloc_zero(mem_ctx, 24);
+               } else {
+                       *lm_response = LMv2_generate_response(mem_ctx,
+                                                             ntlm_v2_hash,
+                                                             server_chal);
+               }
                if (lm_session_key) {
                        *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
 
@@ -522,7 +552,8 @@ bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx,
        E_md4hash(password, nt_hash);
 
        return SMBNTLMv2encrypt_hash(mem_ctx,
-                                    user, domain, nt_hash, server_chal, names_blob,
+                                    user, domain, nt_hash,
+                                    server_chal, NULL, names_blob,
                                     lm_response, nt_response, lm_session_key, user_session_key);
 }
 
@@ -533,7 +564,7 @@ bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx,
 bool encode_pw_buffer(uint8_t buffer[516], const char *password, int string_flags)
 {
        uint8_t new_pw[512];
-       size_t new_pw_len;
+       ssize_t new_pw_len;
 
        /* the incoming buffer can be any alignment. */
        string_flags |= STR_NOALIGN;
@@ -541,6 +572,9 @@ bool encode_pw_buffer(uint8_t buffer[516], const char *password, int string_flag
        new_pw_len = push_string(new_pw,
                                 password,
                                 sizeof(new_pw), string_flags);
+       if (new_pw_len == -1) {
+               return false;
+       }
 
        memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
 
@@ -600,8 +634,7 @@ bool decode_pw_buffer(TALLOC_CTX *ctx,
                                   &in_buffer[512 - byte_len],
                                   byte_len,
                                   (void *)pp_new_pwrd,
-                                  new_pw_len,
-                                  false)) {
+                                  new_pw_len)) {
                DEBUG(0, ("decode_pw_buffer: failed to convert incoming password\n"));
                return false;
        }
@@ -622,7 +655,7 @@ bool decode_pw_buffer(TALLOC_CTX *ctx,
 
 void encode_or_decode_arc4_passwd_buffer(unsigned char pw_buf[532], const DATA_BLOB *psession_key)
 {
-       struct MD5Context tctx;
+       MD5_CTX tctx;
        unsigned char key_out[16];
 
        /* Confounder is last 16 bytes. */
@@ -702,7 +735,7 @@ void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
                                        struct wkssvc_PasswordBuffer **pwd_buf)
 {
        uint8_t buffer[516];
-       struct MD5Context ctx;
+       MD5_CTX ctx;
        struct wkssvc_PasswordBuffer *my_pwd_buf = NULL;
        DATA_BLOB confounded_session_key;
        int confounder_len = 8;
@@ -740,7 +773,7 @@ WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
                                          char **pwd)
 {
        uint8_t buffer[516];
-       struct MD5Context ctx;
+       MD5_CTX ctx;
        size_t pwd_len;
 
        DATA_BLOB confounded_session_key;
@@ -751,12 +784,12 @@ WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
        *pwd = NULL;
 
        if (!pwd_buf) {
-               return WERR_BAD_PASSWORD;
+               return WERR_INVALID_PASSWORD;
        }
 
        if (session_key->length != 16) {
                DEBUG(10,("invalid session key\n"));
-               return WERR_BAD_PASSWORD;
+               return WERR_INVALID_PASSWORD;
        }
 
        confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
@@ -773,7 +806,7 @@ WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
 
        if (!decode_pw_buffer(mem_ctx, buffer, pwd, &pwd_len, CH_UTF16)) {
                data_blob_free(&confounded_session_key);
-               return WERR_BAD_PASSWORD;
+               return WERR_INVALID_PASSWORD;
        }
 
        data_blob_free(&confounded_session_key);