1 /***********************************************************************
2 *
3 * winbind.c
4 *
5 * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
6 * authentication using WINBIND to contact a NT-style PDC.
7 *
8 * Based on the structure of the radius module.
9 *
10 * Copyright (C) 2003 Andrew Bartlet <abartlet@samba.org>
11 *
12 * Copyright 1999 Paul Mackerras, Alan Curry.
13 * (pipe read code from passpromt.c)
14 *
15 * Copyright (C) 2002 Roaring Penguin Software Inc.
16 *
17 * Based on a patch for ipppd, which is:
18 * Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
19 * Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
20 * Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
21 *
22 * Uses radiusclient library, which is:
23 * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
24 * Copyright (C) 2002 Roaring Penguin Software Inc.
25 *
26 * MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
27 * modification from Frank Cusack, <frank@google.com>.
28 *
29 * Updated on 2003-12-12 to support updated PPP plugin API from latest CVS
30 * Copyright (C) 2003, Sean E. Millichamp <sean at bruenor dot org>
31 *
32 * This plugin may be distributed according to the terms of the GNU
33 * General Public License, version 2 or (at your option) any later version.
34 *
35 ***********************************************************************/
36
37 #include "pppd.h"
38 #include "chap-new.h"
39 #include "chap_ms.h"
40 #ifdef MPPE
41 #include "md5.h"
42 #endif
43 #include "fsm.h"
44 #include "ipcp.h"
45 #include <syslog.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <fcntl.h>
49 #include <sys/time.h>
50 #include <sys/wait.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <ctype.h>
56
57 #define BUF_LEN 1024
58
59 #define NOT_AUTHENTICATED 0
60 #define AUTHENTICATED 1
61
62 static char *ntlm_auth = NULL;
63
set_ntlm_auth(char ** argv)64 static int set_ntlm_auth(char **argv)
65 {
66 char *p;
67
68 p = argv[0];
69 if (p[0] != '/') {
70 option_error("ntlm_auth-helper argument must be full path");
71 return 0;
72 }
73 p = strdup(p);
74 if (p == NULL) {
75 novm("ntlm_auth-helper argument");
76 return 0;
77 }
78 if (ntlm_auth != NULL)
79 free(ntlm_auth);
80 ntlm_auth = p;
81 return 1;
82 }
83
84 static option_t Options[] = {
85 { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth,
86 "Path to ntlm_auth executable", OPT_PRIV },
87 { NULL }
88 };
89
90 static int
91 winbind_secret_check(void);
92
93 static int winbind_pap_auth(char *user,
94 char *passwd,
95 char **msgp,
96 struct wordlist **paddrs,
97 struct wordlist **popts);
98 static int winbind_chap_verify(char *user, char *ourname, int id,
99 struct chap_digest_type *digest,
100 unsigned char *challenge,
101 unsigned char *response,
102 char *message, int message_space);
103 static int winbind_allowed_address(u_int32_t addr);
104
105 char pppd_version[] = VERSION;
106
107 /**********************************************************************
108 * %FUNCTION: plugin_init
109 * %ARGUMENTS:
110 * None
111 * %RETURNS:
112 * Nothing
113 * %DESCRIPTION:
114 * Initializes WINBIND plugin.
115 ***********************************************************************/
116 void
plugin_init(void)117 plugin_init(void)
118 {
119 pap_check_hook = winbind_secret_check;
120 pap_auth_hook = winbind_pap_auth;
121
122 chap_check_hook = winbind_secret_check;
123 chap_verify_hook = winbind_chap_verify;
124
125 allowed_address_hook = winbind_allowed_address;
126
127 /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
128 chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
129
130 add_options(Options);
131
132 info("WINBIND plugin initialized.");
133 }
134
135 /**
136 Routine to get hex characters and turn them into a 16 byte array.
137 the array can be variable length, and any non-hex-numeric
138 characters are skipped. "0xnn" or "0Xnn" is specially catered
139 for.
140
141 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
142
143 **/
144
145 /*
146 Unix SMB/CIFS implementation.
147 Samba utility functions
148
149 Copyright (C) Andrew Tridgell 1992-2001
150 Copyright (C) Simo Sorce 2001-2002
151 Copyright (C) Martin Pool 2003
152
153 This program is free software; you can redistribute it and/or modify
154 it under the terms of the GNU General Public License as published by
155 the Free Software Foundation; either version 2 of the License, or
156 (at your option) any later version.
157
158 This program is distributed in the hope that it will be useful,
159 but WITHOUT ANY WARRANTY; without even the implied warranty of
160 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
161 GNU General Public License for more details.
162
163 You should have received a copy of the GNU General Public License
164 along with this program; if not, write to the Free Software
165 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
166 */
167
strhex_to_str(char * p,size_t len,const char * strhex)168 size_t strhex_to_str(char *p, size_t len, const char *strhex)
169 {
170 size_t i;
171 size_t num_chars = 0;
172 unsigned char lonybble, hinybble;
173 const char *hexchars = "0123456789ABCDEF";
174 char *p1 = NULL, *p2 = NULL;
175
176 for (i = 0; i < len && strhex[i] != 0; i++) {
177 if (strncmp(hexchars, "0x", 2) == 0) {
178 i++; /* skip two chars */
179 continue;
180 }
181
182 if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
183 break;
184
185 i++; /* next hex digit */
186
187 if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
188 break;
189
190 /* get the two nybbles */
191 hinybble = (p1 - hexchars);
192 lonybble = (p2 - hexchars);
193
194 p[num_chars] = (hinybble << 4) | lonybble;
195 num_chars++;
196
197 p1 = NULL;
198 p2 = NULL;
199 }
200 return num_chars;
201 }
202
203 static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
204
205 /**
206 * Encode a base64 string into a malloc()ed string caller to free.
207 *
208 *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
209 **/
base64_encode(const char * data)210 char * base64_encode(const char *data)
211 {
212 size_t out_cnt = 0;
213 size_t len = strlen(data);
214 size_t output_len = 4 * ((len + 2) / 3) + 2;
215 const unsigned char *ptr = (const unsigned char *) data;
216 char *result = malloc(output_len); /* get us plenty of space */
217 unsigned int bits;
218
219 for (; len >= 3; len -= 3) {
220 bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
221 ptr += 3;
222 result[out_cnt++] = b64[bits >> 18];
223 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
224 result[out_cnt++] = b64[(bits >> 6) & 0x3f];
225 result[out_cnt++] = b64[bits & 0x3f];
226 }
227 if (len != 0) {
228 bits = ptr[0] << 16;
229 if (len > 1)
230 bits |= ptr[1] << 8;
231 result[out_cnt++] = b64[bits >> 18];
232 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
233 result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '=';
234 result[out_cnt++] = '=';
235 }
236
237 result[out_cnt] = '\0'; /* terminate */
238 return result;
239 }
240
run_ntlm_auth(const char * username,const char * domain,const char * full_username,const char * plaintext_password,const u_char * challenge,size_t challenge_length,const u_char * lm_response,size_t lm_response_length,const u_char * nt_response,size_t nt_response_length,u_char nt_key[16],char ** error_string)241 unsigned int run_ntlm_auth(const char *username,
242 const char *domain,
243 const char *full_username,
244 const char *plaintext_password,
245 const u_char *challenge,
246 size_t challenge_length,
247 const u_char *lm_response,
248 size_t lm_response_length,
249 const u_char *nt_response,
250 size_t nt_response_length,
251 u_char nt_key[16],
252 char **error_string)
253 {
254
255 pid_t forkret;
256 int child_in[2];
257 int child_out[2];
258 int status;
259
260 int authenticated = NOT_AUTHENTICATED; /* not auth */
261 int got_user_session_key = 0; /* not got key */
262
263 char buffer[1024];
264
265 FILE *pipe_in;
266 FILE *pipe_out;
267
268 int i;
269 char *challenge_hex;
270 char *lm_hex_hash;
271 char *nt_hex_hash;
272
273 /* First see if we have a program to run... */
274 if (ntlm_auth == NULL)
275 return NOT_AUTHENTICATED;
276
277 /* Make first child */
278 if (pipe(child_out) == -1) {
279 error("pipe creation failed for child OUT!");
280 return NOT_AUTHENTICATED;
281 }
282
283 if (pipe(child_in) == -1) {
284 error("pipe creation failed for child IN!");
285 return NOT_AUTHENTICATED;
286 }
287
288 forkret = safe_fork(child_in[0], child_out[1], 2);
289 if (forkret == -1) {
290 if (error_string) {
291 *error_string = strdup("fork failed!");
292 }
293
294 return NOT_AUTHENTICATED;
295 }
296
297 if (forkret == 0) {
298 /* child process */
299 uid_t uid;
300
301 close(child_out[0]);
302 close(child_in[1]);
303
304 /* run winbind as the user that invoked pppd */
305 setgid(getgid());
306 uid = getuid();
307 if (setuid(uid) == -1 || getuid() != uid)
308 fatal("pppd/winbind: could not setuid to %d: %m", uid);
309 execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);
310 fatal("pppd/winbind: could not exec /bin/sh: %m");
311 }
312
313 /* parent */
314 close(child_out[1]);
315 close(child_in[0]);
316
317 /* Need to write the User's info onto the pipe */
318
319 pipe_in = fdopen(child_in[1], "w");
320
321 pipe_out = fdopen(child_out[0], "r");
322
323 /* look for session key coming back */
324
325 if (username) {
326 char *b64_username = base64_encode(username);
327 fprintf(pipe_in, "Username:: %s\n", b64_username);
328 free(b64_username);
329 }
330
331 if (domain) {
332 char *b64_domain = base64_encode(domain);
333 fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
334 free(b64_domain);
335 }
336
337 if (full_username) {
338 char *b64_full_username = base64_encode(full_username);
339 fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
340 free(b64_full_username);
341 }
342
343 if (plaintext_password) {
344 char *b64_plaintext_password = base64_encode(plaintext_password);
345 fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
346 free(b64_plaintext_password);
347 }
348
349 if (challenge_length) {
350 fprintf(pipe_in, "Request-User-Session-Key: yes\n");
351
352 challenge_hex = malloc(challenge_length*2+1);
353
354 for (i = 0; i < challenge_length; i++)
355 sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
356
357 fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
358 free(challenge_hex);
359 }
360
361 if (lm_response_length) {
362 lm_hex_hash = malloc(lm_response_length*2+1);
363
364 for (i = 0; i < lm_response_length; i++)
365 sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
366
367 fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
368 free(lm_hex_hash);
369 }
370
371 if (nt_response_length) {
372 nt_hex_hash = malloc(nt_response_length*2+1);
373
374 for (i = 0; i < nt_response_length; i++)
375 sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
376
377 fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
378 free(nt_hex_hash);
379 }
380
381 fprintf(pipe_in, ".\n");
382 fflush(pipe_in);
383
384 while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
385 char *message, *parameter;
386 if (buffer[strlen(buffer)-1] != '\n') {
387 break;
388 }
389 buffer[strlen(buffer)-1] = '\0';
390 message = buffer;
391
392 if (!(parameter = strstr(buffer, ": "))) {
393 break;
394 }
395
396 parameter[0] = '\0';
397 parameter++;
398 parameter[0] = '\0';
399 parameter++;
400
401 if (strcmp(message, ".") == 0) {
402 /* end of sequence */
403 break;
404 } else if (strcasecmp(message, "Authenticated") == 0) {
405 if (strcasecmp(parameter, "Yes") == 0) {
406 authenticated = AUTHENTICATED;
407 } else {
408 notice("Winbind has declined authentication for user!");
409 authenticated = NOT_AUTHENTICATED;
410 }
411 } else if (strcasecmp(message, "User-session-key") == 0) {
412 /* length is the number of characters to parse */
413 if (nt_key) {
414 if (strhex_to_str(nt_key, 32, parameter) == 16) {
415 got_user_session_key = 1;
416 } else {
417 notice("NT session key for user was not 16 bytes!");
418 }
419 }
420 } else if (strcasecmp(message, "Error") == 0) {
421 authenticated = NOT_AUTHENTICATED;
422 if (error_string)
423 *error_string = strdup(parameter);
424 } else if (strcasecmp(message, "Authentication-Error") == 0) {
425 authenticated = NOT_AUTHENTICATED;
426 if (error_string)
427 *error_string = strdup(parameter);
428 } else {
429 notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
430 }
431 }
432
433 /* parent */
434 if (close(child_out[0]) == -1) {
435 notice("error closing pipe?!? for child OUT[0]");
436 return NOT_AUTHENTICATED;
437 }
438
439 /* parent */
440 if (close(child_in[1]) == -1) {
441 notice("error closing pipe?!? for child IN[1]");
442 return NOT_AUTHENTICATED;
443 }
444
445 while ((wait(&status) == -1) && errno == EINTR)
446 ;
447
448 if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
449 notice("Did not get user session key, despite being authenticated!");
450 return NOT_AUTHENTICATED;
451 }
452 return authenticated;
453 }
454
455 /**********************************************************************
456 * %FUNCTION: winbind_secret_check
457 * %ARGUMENTS:
458 * None
459 * %RETURNS:
460 * 0 if we don't have an ntlm_auth program to run, otherwise 1.
461 * %DESCRIPTION:
462 * Tells pppd that we will try to authenticate the peer, and not to
463 * worry about looking in /etc/ppp/ *-secrets
464 ***********************************************************************/
465 static int
winbind_secret_check(void)466 winbind_secret_check(void)
467 {
468 return ntlm_auth != NULL;
469 }
470
471 /**********************************************************************
472 * %FUNCTION: winbind_pap_auth
473 * %ARGUMENTS:
474 * user -- user-name of peer
475 * passwd -- password supplied by peer
476 * msgp -- Message which will be sent in PAP response
477 * paddrs -- set to a list of possible peer IP addresses
478 * popts -- set to a list of additional pppd options
479 * %RETURNS:
480 * 1 if we can authenticate, -1 if we cannot.
481 * %DESCRIPTION:
482 * Performs PAP authentication using WINBIND
483 ***********************************************************************/
484 static int
winbind_pap_auth(char * user,char * password,char ** msgp,struct wordlist ** paddrs,struct wordlist ** popts)485 winbind_pap_auth(char *user,
486 char *password,
487 char **msgp,
488 struct wordlist **paddrs,
489 struct wordlist **popts)
490 {
491 if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
492 return 1;
493 }
494 return -1;
495 }
496
497 /**********************************************************************
498 * %FUNCTION: winbind_chap_auth
499 * %ARGUMENTS:
500 * user -- user-name of peer
501 * remmd -- hash received from peer
502 * remmd_len -- length of remmd
503 * cstate -- pppd's chap_state structure
504 * %RETURNS:
505 * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
506 * %DESCRIPTION:
507 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
508 ***********************************************************************/
509
510 static int
winbind_chap_verify(char * user,char * ourname,int id,struct chap_digest_type * digest,unsigned char * challenge,unsigned char * response,char * message,int message_space)511 winbind_chap_verify(char *user, char *ourname, int id,
512 struct chap_digest_type *digest,
513 unsigned char *challenge,
514 unsigned char *response,
515 char *message, int message_space)
516 {
517 int challenge_len, response_len;
518 char domainname[256];
519 char *domain;
520 char *username;
521 char *p;
522 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
523
524 /* The first byte of each of these strings contains their length */
525 challenge_len = *challenge++;
526 response_len = *response++;
527
528 /* remove domain from "domain\username" */
529 if ((username = strrchr(user, '\\')) != NULL)
530 ++username;
531 else
532 username = user;
533
534 strlcpy(domainname, user, sizeof(domainname));
535
536 /* remove domain from "domain\username" */
537 if ((p = strrchr(domainname, '\\')) != NULL) {
538 *p = '\0';
539 domain = domainname;
540 } else {
541 domain = NULL;
542 }
543
544 /* generate MD based on negotiated type */
545 switch (digest->code) {
546
547 case CHAP_MICROSOFT:
548 {
549 char *error_string = NULL;
550 u_char *nt_response = NULL;
551 u_char *lm_response = NULL;
552 int nt_response_size = 0;
553 int lm_response_size = 0;
554 u_char session_key[16];
555
556 if (response_len != MS_CHAP_RESPONSE_LEN)
557 break; /* not even the right length */
558
559 /* Determine which part of response to verify against */
560 if (response[MS_CHAP_USENT]) {
561 nt_response = &response[MS_CHAP_NTRESP];
562 nt_response_size = MS_CHAP_NTRESP_LEN;
563 } else {
564 #ifdef MSLANMAN
565 lm_response = &response[MS_CHAP_LANMANRESP];
566 lm_response_size = MS_CHAP_LANMANRESP_LEN;
567 #else
568 /* Should really propagate this into the error packet. */
569 notice("Peer request for LANMAN auth not supported");
570 return NOT_AUTHENTICATED;
571 #endif /* MSLANMAN */
572 }
573
574 /* ship off to winbind, and check */
575
576 if (run_ntlm_auth(username,
577 domain,
578 NULL,
579 NULL,
580 challenge, challenge_len,
581 lm_response, lm_response_size,
582 nt_response, nt_response_size,
583 session_key,
584 &error_string) == AUTHENTICATED) {
585 mppe_set_keys(challenge, session_key);
586 slprintf(message, message_space, "Access granted");
587 return AUTHENTICATED;
588
589 } else {
590 if (error_string) {
591 notice(error_string);
592 free(error_string);
593 }
594 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
595 challenge_len, challenge);
596 return NOT_AUTHENTICATED;
597 }
598 break;
599 }
600
601 case CHAP_MICROSOFT_V2:
602 {
603 u_char Challenge[8];
604 u_char session_key[MD4_SIGNATURE_SIZE];
605 char *error_string = NULL;
606
607 if (response_len != MS_CHAP2_RESPONSE_LEN)
608 break; /* not even the right length */
609
610 ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
611 user, Challenge);
612
613 /* ship off to winbind, and check */
614
615 if (run_ntlm_auth(username,
616 domain,
617 NULL,
618 NULL,
619 Challenge, 8,
620 NULL, 0,
621 &response[MS_CHAP2_NTRESP],
622 MS_CHAP2_NTRESP_LEN,
623 session_key,
624 &error_string) == AUTHENTICATED) {
625
626 GenerateAuthenticatorResponse(session_key,
627 &response[MS_CHAP2_NTRESP],
628 &response[MS_CHAP2_PEER_CHALLENGE],
629 challenge, user, saresponse);
630 mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP],
631 MS_CHAP2_AUTHENTICATOR);
632 if (response[MS_CHAP2_FLAGS]) {
633 slprintf(message, message_space, "S=%s", saresponse);
634 } else {
635 slprintf(message, message_space, "S=%s M=%s",
636 saresponse, "Access granted");
637 }
638 return AUTHENTICATED;
639
640 } else {
641 if (error_string) {
642 notice(error_string);
643 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
644 challenge_len, challenge, error_string);
645 free(error_string);
646 } else {
647 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
648 challenge_len, challenge, "Access denied");
649 }
650 return NOT_AUTHENTICATED;
651 }
652 break;
653 }
654
655 default:
656 error("WINBIND: Challenge type %u unsupported", digest->code);
657 }
658 return NOT_AUTHENTICATED;
659 }
660
661 static int
winbind_allowed_address(u_int32_t addr)662 winbind_allowed_address(u_int32_t addr)
663 {
664 ipcp_options *wo = &ipcp_wantoptions[0];
665 if (wo->hisaddr !=0 && wo->hisaddr == addr) {
666 return 1;
667 }
668 return -1;
669 }
670