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