5068540a3274ca848926acffc751c49b8b7c1b3a
[kai/samba.git] / source4 / auth / credentials / credentials_ntlm.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    User credentials handling
5
6    Copyright (C) Andrew Tridgell      2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
8    Copyright (C) Stefan Metzmacher 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "librpc/gen_ndr/ndr_samr.h" /* for struct samrPassword */
27 #include "lib/crypto/crypto.h"
28
29 void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, 
30                                               const char **username, 
31                                               const char **domain) 
32 {
33         if (cred->principal_obtained > cred->username_obtained) {
34                 *domain = talloc_strdup(mem_ctx, "");
35                 *username = cli_credentials_get_principal(cred, mem_ctx);
36         } else {
37                 *domain = cli_credentials_get_domain(cred);
38                 *username = cli_credentials_get_username(cred);
39         }
40 }
41
42 NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, 
43                                            int *flags,
44                                            DATA_BLOB challenge, DATA_BLOB target_info, 
45                                            DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, 
46                                            DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) 
47 {
48         const char *user, *domain;
49         DATA_BLOB lm_response, nt_response;
50         DATA_BLOB lm_session_key, session_key;
51         const struct samr_Password *nt_hash;
52         lm_session_key = data_blob(NULL, 0);
53
54         nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
55
56         cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
57
58         /* If we are sending a username@realm login (see function
59          * above), then we will not send LM, it will not be
60          * accepted */
61         if (cred->principal_obtained > cred->username_obtained) {
62                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
63         }
64
65         /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
66         if (cred->machine_account) {
67                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
68         }
69         
70         if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
71                 return NT_STATUS_ACCESS_DENIED;
72         }
73
74         if (!nt_hash) {
75                 static const uint8_t zeros[16];
76                 /* do nothing - blobs are zero length */
77
78                 /* session key is all zeros */
79                 session_key = data_blob_talloc(mem_ctx, zeros, 16);
80                 lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
81
82                 lm_response = data_blob(NULL, 0);
83                 nt_response = data_blob(NULL, 0);
84                 
85                 /* not doing NTLM2 without a password */
86                 *flags &= ~CLI_CRED_NTLM2;
87         } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
88
89                 if (!target_info.length) {
90                         /* be lazy, match win2k - we can't do NTLMv2 without it */
91                         DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
92                         return NT_STATUS_INVALID_PARAMETER;
93                 }
94
95                 /* TODO: if the remote server is standalone, then we should replace 'domain'
96                    with the server name as supplied above */
97                 
98                 if (!SMBNTLMv2encrypt_hash(mem_ctx,
99                                            user, 
100                                            domain, 
101                                            nt_hash->hash, &challenge, 
102                                            &target_info, 
103                                            &lm_response, &nt_response, 
104                                            NULL, &session_key)) {
105                         return NT_STATUS_NO_MEMORY;
106                 }
107
108                 /* LM Key is incompatible... */
109                 *flags &= ~CLI_CRED_LANMAN_AUTH;
110         } else if (*flags & CLI_CRED_NTLM2) {
111                 struct MD5Context md5_session_nonce_ctx;
112                 uint8_t session_nonce[16];
113                 uint8_t session_nonce_hash[16];
114                 uint8_t user_session_key[16];
115                 
116                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
117                 generate_random_buffer(lm_response.data, 8);
118                 memset(lm_response.data+8, 0, 16);
119
120                 memcpy(session_nonce, challenge.data, 8);
121                 memcpy(&session_nonce[8], lm_response.data, 8);
122         
123                 MD5Init(&md5_session_nonce_ctx);
124                 MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
125                 MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
126                 MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
127
128                 DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
129                 DEBUG(5, ("challenge is: \n"));
130                 dump_data(5, session_nonce_hash, 8);
131                 
132                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
133                 SMBOWFencrypt(nt_hash->hash,
134                               session_nonce_hash,
135                               nt_response.data);
136                 
137                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
138
139                 SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
140                 hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
141                 dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
142
143                 /* LM Key is incompatible... */
144                 *flags &= ~CLI_CRED_LANMAN_AUTH;
145         } else {
146                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
147                 SMBOWFencrypt(nt_hash->hash, challenge.data,
148                               nt_response.data);
149                 
150                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
151                 SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
152                 dump_data_pw("NT session key:\n", session_key.data, session_key.length);
153
154                 /* lanman auth is insecure, it may be disabled.  
155                    We may also not have a password */
156                 if (*flags & CLI_CRED_LANMAN_AUTH) {
157                         const char *password;
158                         password = cli_credentials_get_password(cred);
159                         if (!password) {
160                                 lm_response = nt_response;
161                         } else {
162                                 uint8_t lm_hash[16];
163                                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
164                                 if (!SMBencrypt(password,challenge.data,
165                                                 lm_response.data)) {
166                                         /* If the LM password was too long (and therefore the LM hash being
167                                            of the first 14 chars only), don't send it.
168
169                                            We don't have any better options but to send the NT response 
170                                         */
171                                         data_blob_free(&lm_response);
172                                         lm_response = nt_response;
173                                         /* LM Key is incompatible with 'long' passwords */
174                                         *flags &= ~CLI_CRED_LANMAN_AUTH;
175                                 } else {
176                                         E_deshash(password, lm_hash);
177                                         lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
178                                         memcpy(lm_session_key.data, lm_hash, 8);
179                                         memset(&lm_session_key.data[8], '\0', 8);
180                                         
181                                         if (!(*flags & CLI_CRED_NTLM_AUTH)) {
182                                                 session_key = lm_session_key;
183                                         }
184                                 }
185                         }
186                 } else {
187                         /* LM Key is incompatible... */
188                         lm_response = nt_response;
189                         *flags &= ~CLI_CRED_LANMAN_AUTH;
190                 }
191         }
192         if (_lm_response) {
193                 *_lm_response = lm_response;
194         }
195         if (_nt_response) {
196                 *_nt_response = nt_response;
197         }
198         if (_lm_session_key) {
199                 *_lm_session_key = lm_session_key;
200         }
201         if (_session_key) {
202                 *_session_key = session_key;
203         }
204         return NT_STATUS_OK;
205 }
206