s4-auth: rework map_user_info() to use cracknames
[kai/samba.git] / source4 / auth / ntlm / auth_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Authentication utility functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Andrew Bartlett 2001
6    Copyright (C) Jeremy Allison 2000-2001
7    Copyright (C) Rafal Szczesniak 2002
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 "auth/auth.h"
26 #include "libcli/auth/libcli_auth.h"
27 #include "param/param.h"
28 #include "auth/ntlm/auth_proto.h"
29 #include "librpc/gen_ndr/drsuapi.h"
30 #include "dsdb/samdb/samdb.h"
31
32 /* this default function can be used by mostly all backends
33  * which don't want to set a challenge
34  */
35 NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, uint8_t chal[8])
36 {
37         /* we don't want to set a challenge */
38         return NT_STATUS_NOT_IMPLEMENTED;
39 }
40
41 /****************************************************************************
42  Create an auth_usersupplied_data structure after appropriate mapping.
43 ****************************************************************************/
44 static NTSTATUS map_user_info_cracknames(struct ldb_context *sam_ctx,
45                                          TALLOC_CTX *mem_ctx,
46                                          const char *default_domain,
47                                          const struct auth_usersupplied_info *user_info,
48                                          struct auth_usersupplied_info **user_info_mapped)
49 {
50         char *domain;
51         char *account_name;
52         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
53         WERROR werr;
54         struct drsuapi_DsNameInfo1 info1;
55
56         DEBUG(5,("map_user_info_cracknames: Mapping user [%s]\\[%s] from workstation [%s]\n",
57                  user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
58
59         account_name = talloc_strdup(tmp_ctx, user_info->client.account_name);
60         if (!account_name) {
61                 talloc_free(tmp_ctx);
62                 return NT_STATUS_NO_MEMORY;
63         }
64
65         /* use cracknames to work out what domain is being
66            asked for */
67         if (strchr_m(user_info->client.account_name, '@') != NULL) {
68                 werr = DsCrackNameOneName(sam_ctx, tmp_ctx, 0,
69                                           DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
70                                           DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
71                                           user_info->client.account_name,
72                                           &info1);
73                 if (!W_ERROR_IS_OK(werr)) {
74                         DEBUG(2,("map_user_info: Failed cracknames of account '%s'\n",
75                                  user_info->client.account_name));
76                         talloc_free(tmp_ctx);
77                         return werror_to_ntstatus(werr);
78                 }
79                 switch (info1.status) {
80                 case DRSUAPI_DS_NAME_STATUS_OK:
81                         break;
82                 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
83                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> NOT_FOUND\n",
84                                  user_info->client.account_name));
85                         talloc_free(tmp_ctx);
86                         return NT_STATUS_NO_SUCH_USER;
87                 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
88                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> DOMAIN_ONLY\n",
89                                  user_info->client.account_name));
90                         talloc_free(tmp_ctx);
91                         return NT_STATUS_NO_SUCH_USER;
92                 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
93                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> NOT_UNIQUE\n",
94                                  user_info->client.account_name));
95                         talloc_free(tmp_ctx);
96                         return NT_STATUS_NO_SUCH_USER;
97                 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
98                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> RESOLVE_ERROR\n",
99                                  user_info->client.account_name));
100                         talloc_free(tmp_ctx);
101                         return NT_STATUS_NO_SUCH_USER;
102                 default:
103                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> unknown error %u\n",
104                                  user_info->client.account_name, info1.status));
105                         talloc_free(tmp_ctx);
106                         return NT_STATUS_NO_SUCH_USER;
107                 }
108                 /* info1.result_name is in DOMAIN\username
109                  * form, which we need to split up into the
110                  * user_info_mapped structure
111                  */
112                 domain = talloc_strdup(tmp_ctx, info1.result_name);
113                 if (domain == NULL) {
114                         talloc_free(tmp_ctx);
115                         return NT_STATUS_NO_MEMORY;
116                 }
117                 account_name = strchr_m(domain, '\\');
118                 if (account_name == NULL) {
119                         DEBUG(2,("map_user_info: Cracknames of account '%s' gave invalid result '%s'\n",
120                                  user_info->client.account_name, info1.result_name));
121                         talloc_free(tmp_ctx);
122                         return NT_STATUS_NO_SUCH_USER;
123                 }
124                 *account_name = 0;
125                 account_name = talloc_strdup(tmp_ctx, account_name+1);
126                 if (account_name == NULL) {
127                         talloc_free(tmp_ctx);
128                         return NT_STATUS_NO_MEMORY;
129                 }
130         } else {
131                 char *domain_name;
132                 if (user_info->client.domain_name && *user_info->client.domain_name) {
133                         domain_name = talloc_asprintf(tmp_ctx, "%s\\", user_info->client.domain_name);
134                 } else {
135                         domain_name = talloc_strdup(tmp_ctx, default_domain);
136                 }
137                 if (domain_name == NULL) {
138                         talloc_free(tmp_ctx);
139                         return NT_STATUS_NO_MEMORY;
140                 }
141                 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
142                                           DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
143                                           DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
144                                           domain_name,
145                                           &info1);
146                 if (!W_ERROR_IS_OK(werr)) {
147                         DEBUG(2,("map_user_info: Failed cracknames of domain '%s'\n",
148                                  domain_name));
149                         talloc_free(tmp_ctx);
150                         return werror_to_ntstatus(werr);
151                 }
152
153                 /* we use the account_name as-is, but get the
154                  * domain name from cracknames if possible */
155                 account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
156                 if (account_name == NULL) {
157                         talloc_free(tmp_ctx);
158                         return NT_STATUS_NO_MEMORY;
159                 }
160
161                 switch (info1.status) {
162                 case DRSUAPI_DS_NAME_STATUS_OK:
163                 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
164                         domain = talloc_strdup(tmp_ctx, info1.result_name);
165                         if (domain == NULL) {
166                                 talloc_free(tmp_ctx);
167                                 return NT_STATUS_NO_MEMORY;
168                         }
169                         if (domain[strlen_m(domain)-1] == '\\') {
170                                 domain[strlen_m(domain)-1] = 0;
171                         }
172                         break;
173                 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
174                         /* the domain is unknown - use the
175                            default domain */
176                         domain = talloc_strdup(tmp_ctx, default_domain);
177                         break;
178                 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
179                         DEBUG(2,("map_user_info: Cracknames of domain '%s' -> NOT_UNIQUE\n",
180                                  domain_name));
181                         talloc_free(tmp_ctx);
182                         return NT_STATUS_NO_SUCH_USER;
183                 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
184                         DEBUG(2,("map_user_info: Cracknames of domain '%s' -> RESOLVE_ERROR\n",
185                                  domain_name));
186                         talloc_free(tmp_ctx);
187                         return NT_STATUS_NO_SUCH_USER;
188                 default:
189                         DEBUG(2,("map_user_info: Cracknames of account '%s' -> unknown error %u\n",
190                                  domain_name, info1.status));
191                         talloc_free(tmp_ctx);
192                         return NT_STATUS_NO_SUCH_USER;
193                 }
194                 /* domain and account_name are filled in above */
195         }
196
197         *user_info_mapped = talloc_zero(mem_ctx, struct auth_usersupplied_info);
198         if (!*user_info_mapped) {
199                 talloc_free(tmp_ctx);
200                 return NT_STATUS_NO_MEMORY;
201         }
202         if (!talloc_reference(*user_info_mapped, user_info)) {
203                 talloc_free(tmp_ctx);
204                 return NT_STATUS_NO_MEMORY;
205         }
206         **user_info_mapped = *user_info;
207         (*user_info_mapped)->mapped_state = true;
208         (*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
209         (*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
210         talloc_free(tmp_ctx);
211         if (!(*user_info_mapped)->mapped.domain_name
212             || !(*user_info_mapped)->mapped.account_name) {
213                 return NT_STATUS_NO_MEMORY;
214         }
215
216         return NT_STATUS_OK;
217 }
218
219
220 /****************************************************************************
221  Create an auth_usersupplied_data structure after appropriate mapping.
222 ****************************************************************************/
223 NTSTATUS map_user_info(struct ldb_context *sam_ctx,
224                        TALLOC_CTX *mem_ctx,
225                        const char *default_domain,
226                        const struct auth_usersupplied_info *user_info,
227                        struct auth_usersupplied_info **user_info_mapped)
228 {
229         char *domain;
230         char *account_name;
231         char *d;
232         TALLOC_CTX *tmp_ctx;
233
234         if (sam_ctx != NULL) {
235                 /* if possible, use cracknames to parse the
236                    domain/account */
237                 return map_user_info_cracknames(sam_ctx, mem_ctx, default_domain, user_info, user_info_mapped);
238         }
239
240         DEBUG(0,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s] default_domain=%s\n",
241                  user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name,
242                  default_domain));
243
244         tmp_ctx = talloc_new(mem_ctx);
245
246         account_name = talloc_strdup(tmp_ctx, user_info->client.account_name);
247         if (!account_name) {
248                 talloc_free(tmp_ctx);
249                 return NT_STATUS_NO_MEMORY;
250         }
251         
252         /* don't allow "" as a domain, fixes a Win9X bug where it
253            doesn't supply a domain for logon script 'net use'
254            commands.  */
255
256         /* Split user@realm names into user and realm components.
257          * This is TODO to fix with proper userprincipalname
258          * support */
259         if (user_info->client.domain_name && *user_info->client.domain_name) {
260                 domain = talloc_strdup(tmp_ctx, user_info->client.domain_name);
261         } else if (strchr_m(user_info->client.account_name, '@')) {
262                 d = strchr_m(account_name, '@');
263                 if (!d) {
264                         talloc_free(tmp_ctx);
265                         return NT_STATUS_INTERNAL_ERROR;
266                 }
267                 d[0] = '\0';
268                 d++;
269                 domain = d;
270         } else {
271                 domain = talloc_strdup(tmp_ctx, default_domain);
272         }
273
274         if (domain == NULL) {
275                 talloc_free(tmp_ctx);
276                 return NT_STATUS_NO_MEMORY;
277         }
278         *user_info_mapped = talloc_zero(mem_ctx, struct auth_usersupplied_info);
279         if (!*user_info_mapped) {
280                 talloc_free(tmp_ctx);
281                 return NT_STATUS_NO_MEMORY;
282         }
283         if (!talloc_reference(*user_info_mapped, user_info)) {
284                 talloc_free(tmp_ctx);
285                 return NT_STATUS_NO_MEMORY;
286         }
287         **user_info_mapped = *user_info;
288         (*user_info_mapped)->mapped_state = true;
289         (*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
290         (*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
291         talloc_free(tmp_ctx);
292         if (!(*user_info_mapped)->mapped.domain_name 
293             || !(*user_info_mapped)->mapped.account_name) {
294                 return NT_STATUS_NO_MEMORY;
295         }
296
297         return NT_STATUS_OK;
298 }
299
300 /****************************************************************************
301  Create an auth_usersupplied_data structure after appropriate mapping.
302 ****************************************************************************/
303
304 NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth4_context *auth_context, 
305                            enum auth_password_state to_state,
306                            const struct auth_usersupplied_info *user_info_in,
307                            const struct auth_usersupplied_info **user_info_encrypted)
308 {
309         NTSTATUS nt_status;
310         struct auth_usersupplied_info *user_info_temp;
311         switch (to_state) {
312         case AUTH_PASSWORD_RESPONSE:
313                 switch (user_info_in->password_state) {
314                 case AUTH_PASSWORD_PLAIN:
315                 {
316                         const struct auth_usersupplied_info *user_info_temp2;
317                         nt_status = encrypt_user_info(mem_ctx, auth_context, 
318                                                       AUTH_PASSWORD_HASH, 
319                                                       user_info_in, &user_info_temp2);
320                         if (!NT_STATUS_IS_OK(nt_status)) {
321                                 return nt_status;
322                         }
323                         user_info_in = user_info_temp2;
324                         /* fall through */
325                 }
326                 case AUTH_PASSWORD_HASH:
327                 {
328                         uint8_t chal[8];
329                         DATA_BLOB chall_blob;
330                         user_info_temp = talloc_zero(mem_ctx, struct auth_usersupplied_info);
331                         if (!user_info_temp) {
332                                 return NT_STATUS_NO_MEMORY;
333                         }
334                         if (!talloc_reference(user_info_temp, user_info_in)) {
335                                 return NT_STATUS_NO_MEMORY;
336                         }
337                         *user_info_temp = *user_info_in;
338                         user_info_temp->mapped_state = to_state;
339                         
340                         nt_status = auth_get_challenge(auth_context, chal);
341                         if (!NT_STATUS_IS_OK(nt_status)) {
342                                 return nt_status;
343                         }
344                         
345                         chall_blob = data_blob_talloc(mem_ctx, chal, 8);
346                         if (lpcfg_client_ntlmv2_auth(auth_context->lp_ctx)) {
347                                 DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx,  lpcfg_netbios_name(auth_context->lp_ctx), lpcfg_workgroup(auth_context->lp_ctx));
348                                 DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
349                                 
350                                 if (!SMBNTLMv2encrypt_hash(user_info_temp,
351                                                            user_info_in->client.account_name, 
352                                                            user_info_in->client.domain_name, 
353                                                            user_info_in->password.hash.nt->hash, &chall_blob,
354                                                            &names_blob,
355                                                            &lmv2_response, &ntlmv2_response, 
356                                                            &lmv2_session_key, &ntlmv2_session_key)) {
357                                         data_blob_free(&names_blob);
358                                         return NT_STATUS_NO_MEMORY;
359                                 }
360                                 data_blob_free(&names_blob);
361                                 user_info_temp->password.response.lanman = lmv2_response;
362                                 user_info_temp->password.response.nt = ntlmv2_response;
363                                 
364                                 data_blob_free(&lmv2_session_key);
365                                 data_blob_free(&ntlmv2_session_key);
366                         } else {
367                                 DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
368                                 SMBOWFencrypt(user_info_in->password.hash.nt->hash, chal, blob.data);
369
370                                 user_info_temp->password.response.nt = blob;
371                                 if (lpcfg_client_lanman_auth(auth_context->lp_ctx) && user_info_in->password.hash.lanman) {
372                                         DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
373                                         SMBOWFencrypt(user_info_in->password.hash.lanman->hash, chal, blob.data);
374                                         user_info_temp->password.response.lanman = lm_blob;
375                                 } else {
376                                         /* if not sending the LM password, send the NT password twice */
377                                         user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
378                                 }
379                         }
380
381                         user_info_in = user_info_temp;
382                         /* fall through */
383                 }
384                 case AUTH_PASSWORD_RESPONSE:
385                         *user_info_encrypted = user_info_in;
386                 }
387                 break;
388         case AUTH_PASSWORD_HASH:
389         {       
390                 switch (user_info_in->password_state) {
391                 case AUTH_PASSWORD_PLAIN:
392                 {
393                         struct samr_Password lanman;
394                         struct samr_Password nt;
395                         
396                         user_info_temp = talloc_zero(mem_ctx, struct auth_usersupplied_info);
397                         if (!user_info_temp) {
398                                 return NT_STATUS_NO_MEMORY;
399                         }
400                         if (!talloc_reference(user_info_temp, user_info_in)) {
401                                 return NT_STATUS_NO_MEMORY;
402                         }
403                         *user_info_temp = *user_info_in;
404                         user_info_temp->mapped_state = to_state;
405                         
406                         if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
407                                 user_info_temp->password.hash.lanman = talloc(user_info_temp,
408                                                                               struct samr_Password);
409                                 *user_info_temp->password.hash.lanman = lanman;
410                         } else {
411                                 user_info_temp->password.hash.lanman = NULL;
412                         }
413                         
414                         E_md4hash(user_info_in->password.plaintext, nt.hash);
415                         user_info_temp->password.hash.nt = talloc(user_info_temp,
416                                                                    struct samr_Password);
417                         *user_info_temp->password.hash.nt = nt;
418                         
419                         user_info_in = user_info_temp;
420                         /* fall through */
421                 }
422                 case AUTH_PASSWORD_HASH:
423                         *user_info_encrypted = user_info_in;
424                         break;
425                 default:
426                         return NT_STATUS_INVALID_PARAMETER;
427                         break;
428                 }
429                 break;
430         }
431         default:
432                 return NT_STATUS_INVALID_PARAMETER;
433         }
434
435         return NT_STATUS_OK;
436 }