auth/credentials: add cli_credentials_set_utf16_password()
[obnox/samba/samba-obnox.git] / 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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
26 #include "../lib/crypto/crypto.h"
27 #include "libcli/auth/libcli_auth.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30
31 _PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, 
32                                            int *flags,
33                                            DATA_BLOB challenge, DATA_BLOB target_info, 
34                                            DATA_BLOB *_lm_response, DATA_BLOB *_nt_response, 
35                                            DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key) 
36 {
37         const char *user, *domain;
38         DATA_BLOB lm_response, nt_response;
39         DATA_BLOB lm_session_key, session_key;
40         const struct samr_Password *nt_hash;
41         lm_session_key = data_blob(NULL, 0);
42
43         /* We may already have an NTLM response we prepared earlier.
44          * This is used for NTLM pass-though authentication */
45         if (cred->nt_response.data || cred->lm_response.data) {
46                 *_nt_response = cred->nt_response;
47                 *_lm_response = cred->lm_response;
48
49                 if (!cred->lm_response.data) {
50                         *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
51                 }
52                 *_lm_session_key = data_blob(NULL, 0);
53                 *_session_key = data_blob(NULL, 0);
54                 return NT_STATUS_OK;
55         }
56
57         nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
58
59         cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
60
61         /* If we are sending a username@realm login (see function
62          * above), then we will not send LM, it will not be
63          * accepted */
64         if (cred->principal_obtained > cred->username_obtained) {
65                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
66         }
67
68         /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
69         if (cred->machine_account) {
70                 *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
71         }
72         
73         if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
74                 return NT_STATUS_ACCESS_DENIED;
75         }
76
77         if (!nt_hash) {
78                 static const uint8_t zeros[16];
79                 /* do nothing - blobs are zero length */
80
81                 /* session key is all zeros */
82                 session_key = data_blob_talloc(mem_ctx, zeros, 16);
83                 lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
84
85                 lm_response = data_blob(NULL, 0);
86                 nt_response = data_blob(NULL, 0);
87                 
88                 /* not doing NTLM2 without a password */
89                 *flags &= ~CLI_CRED_NTLM2;
90         } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
91
92                 if (!target_info.length) {
93                         /* be lazy, match win2k - we can't do NTLMv2 without it */
94                         DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
95                         return NT_STATUS_INVALID_PARAMETER;
96                 }
97
98                 /* TODO: if the remote server is standalone, then we should replace 'domain'
99                    with the server name as supplied above */
100                 
101                 if (!SMBNTLMv2encrypt_hash(mem_ctx,
102                                            user, 
103                                            domain, 
104                                            nt_hash->hash, &challenge, 
105                                            &target_info, 
106                                            &lm_response, &nt_response, 
107                                            NULL, &session_key)) {
108                         return NT_STATUS_NO_MEMORY;
109                 }
110
111                 /* LM Key is incompatible... */
112                 *flags &= ~CLI_CRED_LANMAN_AUTH;
113         } else if (*flags & CLI_CRED_NTLM2) {
114                 MD5_CTX md5_session_nonce_ctx;
115                 uint8_t session_nonce[16];
116                 uint8_t session_nonce_hash[16];
117                 uint8_t user_session_key[16];
118                 
119                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
120                 generate_random_buffer(lm_response.data, 8);
121                 memset(lm_response.data+8, 0, 16);
122
123                 memcpy(session_nonce, challenge.data, 8);
124                 memcpy(&session_nonce[8], lm_response.data, 8);
125         
126                 MD5Init(&md5_session_nonce_ctx);
127                 MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
128                 MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
129                 MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
130
131                 DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
132                 DEBUG(5, ("challenge is: \n"));
133                 dump_data(5, session_nonce_hash, 8);
134                 
135                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
136                 SMBOWFencrypt(nt_hash->hash,
137                               session_nonce_hash,
138                               nt_response.data);
139                 
140                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
141
142                 SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
143                 hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
144                 dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
145
146                 /* LM Key is incompatible... */
147                 *flags &= ~CLI_CRED_LANMAN_AUTH;
148         } else {
149                 uint8_t lm_hash[16];
150                 nt_response = data_blob_talloc(mem_ctx, NULL, 24);
151                 SMBOWFencrypt(nt_hash->hash, challenge.data,
152                               nt_response.data);
153                 
154                 session_key = data_blob_talloc(mem_ctx, NULL, 16);
155                 SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
156                 dump_data_pw("NT session key:\n", session_key.data, session_key.length);
157
158                 /* lanman auth is insecure, it may be disabled.  
159                    We may also not have a password */
160                 if (*flags & CLI_CRED_LANMAN_AUTH) {
161                         const char *password;
162                         password = cli_credentials_get_password(cred);
163                         if (!password) {
164                                 lm_response = nt_response;
165                         } else {
166                                 lm_response = data_blob_talloc(mem_ctx, NULL, 24);
167                                 if (!SMBencrypt(password,challenge.data,
168                                                 lm_response.data)) {
169                                         /* If the LM password was too long (and therefore the LM hash being
170                                            of the first 14 chars only), don't send it.
171
172                                            We don't have any better options but to send the NT response 
173                                         */
174                                         data_blob_free(&lm_response);
175                                         lm_response = nt_response;
176                                         /* LM Key is incompatible with 'long' passwords */
177                                         *flags &= ~CLI_CRED_LANMAN_AUTH;
178                                 } else if (E_deshash(password, lm_hash)) {
179                                         lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
180                                         memcpy(lm_session_key.data, lm_hash, 8);
181                                         memset(&lm_session_key.data[8], '\0', 8);
182                                         
183                                         if (!(*flags & CLI_CRED_NTLM_AUTH)) {
184                                                 session_key = lm_session_key;
185                                         }
186                                 }
187                         }
188                 } else {
189                         const char *password;
190
191                         /* LM Key is incompatible... */
192                         lm_response = nt_response;
193                         *flags &= ~CLI_CRED_LANMAN_AUTH;
194
195                         password = cli_credentials_get_password(cred);
196                         if (password && E_deshash(password, lm_hash)) {
197                                 lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
198                                 memcpy(lm_session_key.data, lm_hash, 8);
199                                 memset(&lm_session_key.data[8], '\0', 8);
200                         }
201                 }
202         }
203         if (_lm_response) {
204                 *_lm_response = lm_response;
205         }
206         if (_nt_response) {
207                 *_nt_response = nt_response;
208         }
209         if (_lm_session_key) {
210                 *_lm_session_key = lm_session_key;
211         }
212         if (_session_key) {
213                 *_session_key = session_key;
214         }
215         return NT_STATUS_OK;
216 }
217
218 /*
219  * Set a utf16 password on the credentials context, including an indication
220  * of 'how' the password was obtained
221  *
222  * This is required because the nt_hash is calculated over the raw utf16 blob,
223  * which might not be completely valid utf16, which means the conversion
224  * from CH_UTF16MUNGED to CH_UTF8 might loose information.
225  */
226 _PUBLIC_ bool cli_credentials_set_utf16_password(struct cli_credentials *cred,
227                                                  const DATA_BLOB *password_utf16,
228                                                  enum credentials_obtained obtained)
229 {
230         if (password_utf16 == NULL) {
231                 return cli_credentials_set_password(cred, NULL, obtained);
232         }
233
234         if (obtained >= cred->password_obtained) {
235                 struct samr_Password *nt_hash = NULL;
236                 char *password_talloc = NULL;
237                 size_t password_len = 0;
238                 bool ok;
239
240                 nt_hash = talloc(cred, struct samr_Password);
241                 if (nt_hash == NULL) {
242                         return false;
243                 }
244
245                 ok = convert_string_talloc(cred,
246                                            CH_UTF16MUNGED, CH_UTF8,
247                                            password_utf16->data,
248                                            password_utf16->length,
249                                            (void *)&password_talloc,
250                                            &password_len);
251                 if (!ok) {
252                         TALLOC_FREE(nt_hash);
253                         return false;
254                 }
255
256                 ok = cli_credentials_set_password(cred, password_talloc, obtained);
257                 TALLOC_FREE(password_talloc);
258                 if (!ok) {
259                         TALLOC_FREE(nt_hash);
260                         return false;
261                 }
262
263                 mdfour(nt_hash->hash, password_utf16->data, password_utf16->length);
264                 cred->nt_hash = nt_hash;
265                 return true;
266         }
267
268         return false;
269 }
270
271 _PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
272                                  const struct samr_Password *nt_hash, 
273                                  enum credentials_obtained obtained)
274 {
275         if (obtained >= cred->password_obtained) {
276                 cli_credentials_set_password(cred, NULL, obtained);
277                 if (nt_hash) {
278                         cred->nt_hash = talloc(cred, struct samr_Password);
279                         *cred->nt_hash = *nt_hash;
280                 } else {
281                         cred->nt_hash = NULL;
282                 }
283                 return true;
284         }
285
286         return false;
287 }
288
289 _PUBLIC_ bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
290                                                 const DATA_BLOB *lm_response, 
291                                                 const DATA_BLOB *nt_response, 
292                                                 enum credentials_obtained obtained)
293 {
294         if (obtained >= cred->password_obtained) {
295                 cli_credentials_set_password(cred, NULL, obtained);
296                 if (nt_response) {
297                         cred->nt_response = data_blob_talloc(cred, nt_response->data, nt_response->length);
298                         talloc_steal(cred, cred->nt_response.data);
299                 }
300                 if (nt_response) {
301                         cred->lm_response = data_blob_talloc(cred, lm_response->data, lm_response->length);
302                 }
303                 return true;
304         }
305
306         return false;
307 }
308