This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
[kai/samba.git] / source / utils / ntlm_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind status program.
5
6    Copyright (C) Tim Potter      2000-2002
7    Copyright (C) Andrew Bartlett 2003
8    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
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
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 #define SQUID_BUFFER_SIZE 2010
31
32 enum squid_mode {
33         SQUID_2_4_BASIC,
34         SQUID_2_5_BASIC,
35         SQUID_2_5_NTLMSSP
36 };
37         
38
39 extern int winbindd_fd;
40
41 static const char *helper_protocol;
42 static const char *username;
43 static const char *domain;
44 static const char *workstation;
45 static const char *hex_challenge;
46 static const char *hex_lm_response;
47 static const char *hex_nt_response;
48 static unsigned char *challenge;
49 static size_t challenge_len;
50 static unsigned char *lm_response;
51 static size_t lm_response_len;
52 static unsigned char *nt_response;
53 static size_t nt_response_len;
54 static int request_lm_key;
55 static int request_nt_key;
56
57 static char *password;
58
59 static char winbind_separator(void)
60 {
61         struct winbindd_response response;
62         static BOOL got_sep;
63         static char sep;
64
65         if (got_sep)
66                 return sep;
67
68         ZERO_STRUCT(response);
69
70         /* Send off request */
71
72         if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
73             NSS_STATUS_SUCCESS) {
74                 d_printf("could not obtain winbind separator!\n");
75                 return '\\';
76         }
77
78         sep = response.data.info.winbind_separator;
79         got_sep = True;
80
81         if (!sep) {
82                 d_printf("winbind separator was NULL!\n");
83                 return '\\';
84         }
85         
86         return sep;
87 }
88
89 static const char *get_winbind_domain(void)
90 {
91         struct winbindd_response response;
92
93         static fstring winbind_domain;
94         if (*winbind_domain) {
95                 return winbind_domain;
96         }
97
98         ZERO_STRUCT(response);
99
100         /* Send off request */
101
102         if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
103             NSS_STATUS_SUCCESS) {
104                 d_printf("could not obtain winbind domain name!\n");
105                 return NULL;
106         }
107
108         fstrcpy(winbind_domain, response.data.domain_name);
109
110         return winbind_domain;
111
112 }
113
114 static const char *get_winbind_netbios_name(void)
115 {
116         struct winbindd_response response;
117
118         static fstring winbind_netbios_name;
119
120         if (*winbind_netbios_name) {
121                 return winbind_netbios_name;
122         }
123
124         ZERO_STRUCT(response);
125
126         /* Send off request */
127
128         if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
129             NSS_STATUS_SUCCESS) {
130                 d_printf("could not obtain winbind netbios name!\n");
131                 return NULL;
132         }
133
134         fstrcpy(winbind_netbios_name, response.data.netbios_name);
135
136         return winbind_netbios_name;
137
138 }
139
140 /* Authenticate a user with a plaintext password */
141
142 static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics)
143 {
144         struct winbindd_request request;
145         struct winbindd_response response;
146         NSS_STATUS result;
147
148         /* Send off request */
149
150         ZERO_STRUCT(request);
151         ZERO_STRUCT(response);
152
153         fstrcpy(request.data.auth.user, user);
154         fstrcpy(request.data.auth.pass, pass);
155
156         result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
157
158         /* Display response */
159         
160         if (stdout_diagnostics) {
161                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
162                         d_printf("Reading winbind reply failed! (0x01)\n");
163                 }
164                 
165                 d_printf("%s (0x%x)\n", 
166                          response.data.auth.nt_status_string, 
167                          response.data.auth.nt_status);
168         } else {
169                 if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
170                         DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
171                 }
172                 
173                 DEBUG(3, ("%s (0x%x)\n", 
174                          response.data.auth.nt_status_string, 
175                          response.data.auth.nt_status));                
176         }
177                 
178         return (result == NSS_STATUS_SUCCESS);
179 }
180
181 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) 
182 {
183         struct winbindd_request request;
184         struct winbindd_response response;
185         NSS_STATUS result;
186         /* Send off request */
187
188         ZERO_STRUCT(request);
189         ZERO_STRUCT(response);
190
191         fstrcpy(request.data.auth_crap.user, ntlmssp_state->user);
192
193         fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain);
194         fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation);
195         
196         memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data, 
197                MIN(ntlmssp_state->chal.length, 8));
198
199         memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, 
200                MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp)));
201         
202         memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->nt_resp.data,
203                MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp)));
204         
205         request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length;
206         request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length;
207
208         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
209
210         /* Display response */
211
212         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
213                 return NT_STATUS_UNSUCCESSFUL;
214         }
215
216         return NT_STATUS(response.data.auth.nt_status);
217 }
218
219 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
220                                          char *buf, int length) 
221 {
222         static NTLMSSP_STATE *ntlmssp_state = NULL;
223         DATA_BLOB request, reply;
224         NTSTATUS nt_status;
225
226         if (strlen(buf) < 2) {
227                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
228                 x_fprintf(x_stdout, "BH\n");
229                 return;
230         }
231
232         if (strlen(buf) > 3) {
233                 request = base64_decode_data_blob(buf + 3);
234         } else if (strcmp(buf, "YR") == 0) {
235                 request = data_blob(NULL, 0);
236                 if (ntlmssp_state)
237                         ntlmssp_server_end(&ntlmssp_state);
238         } else {
239                 DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
240                 x_fprintf(x_stdout, "BH\n");
241                 return;
242         }
243
244         if (!ntlmssp_state) {
245                 ntlmssp_server_start(&ntlmssp_state);
246                 ntlmssp_state->check_password = winbind_pw_check;
247                 ntlmssp_state->get_domain = get_winbind_domain;
248                 ntlmssp_state->get_global_myname = get_winbind_netbios_name;
249         }
250
251         DEBUG(10, ("got NTLMSSP packet:\n"));
252         dump_data(10, request.data, request.length);
253
254         nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
255         
256         if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
257                 char *reply_base64 = base64_encode_data_blob(reply);
258                 x_fprintf(x_stdout, "TT %s\n", reply_base64);
259                 SAFE_FREE(reply_base64);
260                 data_blob_free(&reply);
261                 DEBUG(10, ("NTLMSSP challenge\n"));
262         } else if (!NT_STATUS_IS_OK(nt_status)) {
263                 x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
264                 DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
265         } else {
266                 x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
267                 DEBUG(10, ("NTLMSSP OK!\n"));
268         }
269
270         data_blob_free(&request);
271 }
272
273 static void manage_squid_basic_request(enum squid_mode squid_mode, 
274                                        char *buf, int length) 
275 {
276         char *user, *pass;      
277         user=buf;
278         
279         pass=memchr(buf,' ',length);
280         if (!pass) {
281                 DEBUG(2, ("Password not found. Denying access\n"));
282                 x_fprintf(x_stderr, "ERR\n");
283                 return;
284         }
285         *pass='\0';
286         pass++;
287         
288         if (squid_mode == SQUID_2_5_BASIC) {
289                 rfc1738_unescape(user);
290                 rfc1738_unescape(pass);
291         }
292         
293         if (check_plaintext_auth(user, pass, False)) {
294                 x_fprintf(x_stdout, "OK\n");
295         } else {
296                 x_fprintf(x_stdout, "ERR\n");
297         }
298 }
299
300 static void manage_squid_request(enum squid_mode squid_mode) 
301 {
302         char buf[SQUID_BUFFER_SIZE+1];
303         int length;
304         char *c;
305         static BOOL err;
306
307         /* this is not a typo - x_fgets doesn't work too well under squid */
308         if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
309                 DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
310                           strerror(ferror(stdin))));
311                 exit(1);    /* BIIG buffer */
312         }
313     
314         c=memchr(buf,'\n',sizeof(buf)-1);
315         if (c) {
316                 *c = '\0';
317                 length = c-buf;
318         } else {
319                 err = 1;
320                 return;
321         }
322         if (err) {
323                 DEBUG(2, ("Oversized message\n"));
324                 x_fprintf(x_stderr, "ERR\n");
325                 err = 0;
326                 return;
327         }
328
329         DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
330
331         if (buf[0] == '\0') {
332                 DEBUG(2, ("Invalid Request\n"));
333                 x_fprintf(x_stderr, "ERR\n");
334                 return;
335         }
336         
337         if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) {
338                 manage_squid_basic_request(squid_mode, buf, length);
339         } else if (squid_mode == SQUID_2_5_NTLMSSP) {
340                 manage_squid_ntlmssp_request(squid_mode, buf, length);
341         }
342 }
343
344
345 static void squid_stream(enum squid_mode squid_mode) {
346         /* initialize FDescs */
347         x_setbuf(x_stdout, NULL);
348         x_setbuf(x_stderr, NULL);
349         while(1) {
350                 manage_squid_request(squid_mode);
351         }
352 }
353
354
355 /* Authenticate a user with a challenge/response */
356
357 static BOOL check_auth_crap(void)
358 {
359         struct winbindd_request request;
360         struct winbindd_response response;
361         char *lm_key;
362         char *nt_key;
363         static uint8 zeros[16];
364
365         NSS_STATUS result;
366         /* Send off request */
367
368         ZERO_STRUCT(request);
369         ZERO_STRUCT(response);
370
371         if (request_lm_key) 
372                 request.data.auth_crap.flags |= WINBIND_PAM_LMKEY;
373
374         if (request_nt_key) 
375                 request.data.auth_crap.flags |= WINBIND_PAM_NTKEY;
376
377         fstrcpy(request.data.auth_crap.user, username);
378
379         fstrcpy(request.data.auth_crap.domain, domain);
380         fstrcpy(request.data.auth_crap.workstation, workstation);
381         
382         memcpy(request.data.auth_crap.chal, challenge, MIN(challenge_len, 8));
383
384         memcpy(request.data.auth_crap.lm_resp, lm_response, MIN(lm_response_len, sizeof(request.data.auth_crap.lm_resp)));
385         
386         memcpy(request.data.auth_crap.nt_resp, nt_response, MIN(nt_response_len, sizeof(request.data.auth_crap.nt_resp)));
387         
388         request.data.auth_crap.lm_resp_len = lm_response_len;
389         request.data.auth_crap.nt_resp_len = nt_response_len;
390
391         result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
392
393         /* Display response */
394
395         if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
396                 d_printf("Reading winbind reply failed! (0x01)\n");
397         }
398
399         d_printf("%s (0x%x)\n", 
400                  response.data.auth.nt_status_string, 
401                  response.data.auth.nt_status);
402
403         if (response.data.auth.nt_status == 0) {
404                 if (request_lm_key 
405                     && (memcmp(zeros, response.data.auth.first_8_lm_hash, 
406                               sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
407                         hex_encode(response.data.auth.first_8_lm_hash, 
408                                    sizeof(response.data.auth.first_8_lm_hash),
409                                    &lm_key);
410                         d_printf("LM_KEY: %s\n", lm_key);
411                         SAFE_FREE(lm_key);
412                 }
413                 if (request_nt_key 
414                     && (memcmp(zeros, response.data.auth.nt_session_key, 
415                               sizeof(response.data.auth.nt_session_key)) != 0)) {
416                         hex_encode(response.data.auth.nt_session_key, 
417                                    sizeof(response.data.auth.nt_session_key), 
418                                    &nt_key);
419                         d_printf("NT_KEY: %s\n", nt_key);
420                         SAFE_FREE(nt_key);
421                 }
422         }
423
424         return result == NSS_STATUS_SUCCESS;
425 }
426
427 /* Main program */
428
429 enum {
430         OPT_USERNAME = 1000,
431         OPT_DOMAIN,
432         OPT_WORKSTATION,
433         OPT_CHALLENGE,
434         OPT_RESPONSE,
435         OPT_LM,
436         OPT_NT,
437         OPT_PASSWORD,
438         OPT_LM_KEY,
439         OPT_NT_KEY
440 };
441
442 int main(int argc, const char **argv)
443 {
444         int opt;
445
446         poptContext pc;
447         struct poptOption long_options[] = {
448                 POPT_AUTOHELP
449
450                 { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
451                 { "username", 0, POPT_ARG_STRING, &username, OPT_USERNAME, "username"},
452                 { "domain", 0, POPT_ARG_STRING, &domain, OPT_DOMAIN, "domain name"},
453                 { "workstation", 0, POPT_ARG_STRING, &domain, OPT_WORKSTATION, "workstation"},
454                 { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
455                 { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
456                 { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
457                 { "password", 0, POPT_ARG_STRING, &password, OPT_PASSWORD, "User's plaintext password"},                
458                 { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
459                 { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"},
460                 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
461                 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
462                 { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
463                 { 0, 0, 0, 0 }
464         };
465
466         /* Samba client initialisation */
467
468         dbf = x_stderr;
469         
470         /* Parse options */
471
472         pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
473
474         /* Parse command line options */
475
476         if (argc == 1) {
477                 poptPrintHelp(pc, stderr, 0);
478                 return 1;
479         }
480
481         pc = poptGetContext(NULL, argc, (const char **)argv, long_options, 
482                             POPT_CONTEXT_KEEP_FIRST);
483
484         while((opt = poptGetNextOpt(pc)) != -1) {
485                 switch (opt) {
486                 case OPT_CHALLENGE:
487                         challenge_len = strlen(hex_challenge);
488                         challenge = smb_xmalloc((challenge_len+1)/2);
489                         if ((challenge_len = strhex_to_str(challenge, challenge_len, hex_challenge)) != 8) {
490                                 fprintf(stderr, "hex decode of %s failed (only got %u bytes)!\n", 
491                                         hex_challenge, challenge_len);
492                                 exit(1);
493                         }
494                         break;
495                 case OPT_LM: 
496                         lm_response_len = strlen(hex_lm_response);
497                         lm_response = smb_xmalloc((lm_response_len+1)/2);
498                         if ((lm_response_len = strhex_to_str(lm_response, lm_response_len, hex_lm_response)) != 24) {
499                                 fprintf(stderr, "hex decode of %s failed!\n", hex_lm_response);
500                                 exit(1);
501                         }
502                         break;
503                 case OPT_NT: 
504                         nt_response_len = strlen(hex_nt_response);
505                         nt_response = smb_xmalloc((nt_response_len+1)/2);
506                         if ((nt_response_len = strhex_to_str(nt_response, nt_response_len, hex_nt_response)) < 24) {
507                                 fprintf(stderr, "hex decode of %s failed!\n", hex_nt_response);
508                                 exit(1);
509                         }
510                         break;
511                 }
512         }
513
514         if (helper_protocol) {
515                 if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) {
516                         squid_stream(SQUID_2_5_NTLMSSP);
517                 } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) {
518                         squid_stream(SQUID_2_5_BASIC);
519                 } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
520                         squid_stream(SQUID_2_4_BASIC);
521                 } else {
522                         fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol);
523                         exit(1);
524                 }
525         }
526
527         if (domain == NULL) {
528                 domain = get_winbind_domain();
529         }
530
531         if (workstation == NULL) {
532                 workstation = "";
533         }
534
535         if (challenge) {
536                 if (!check_auth_crap()) {
537                         exit(1);
538                 }
539         } else if (password) {
540                 fstring user;
541                 snprintf(user, sizeof(user)-1, "%s%c%s", domain, winbind_separator(), username);
542                 if (!check_plaintext_auth(user, password, True)) {
543                         exit(1);
544                 }
545         }
546
547         /* Exit code */
548
549         poptFreeContext(pc);
550         return 0;
551 }