• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	int bits = 0;
213 	int char_count = 0;
214 	size_t out_cnt = 0;
215 	size_t len = strlen(data);
216 	size_t output_len = strlen(data) * 2;
217 	char *result = malloc(output_len); /* get us plenty of space */
218 
219 	while (len-- && out_cnt < (output_len) - 5) {
220 		int c = (unsigned char) *(data++);
221 		bits += c;
222 		char_count++;
223 		if (char_count == 3) {
224 			result[out_cnt++] = b64[bits >> 18];
225 			result[out_cnt++] = b64[(bits >> 12) & 0x3f];
226 			result[out_cnt++] = b64[(bits >> 6) & 0x3f];
227 	    result[out_cnt++] = b64[bits & 0x3f];
228 	    bits = 0;
229 	    char_count = 0;
230 	} else {
231 	    bits <<= 8;
232 	}
233     }
234     if (char_count != 0) {
235 	bits <<= 16 - (8 * char_count);
236 	result[out_cnt++] = b64[bits >> 18];
237 	result[out_cnt++] = b64[(bits >> 12) & 0x3f];
238 	if (char_count == 1) {
239 	    result[out_cnt++] = '=';
240 	    result[out_cnt++] = '=';
241 	} else {
242 	    result[out_cnt++] = b64[(bits >> 6) & 0x3f];
243 	    result[out_cnt++] = '=';
244 	}
245     }
246     result[out_cnt] = '\0';	/* terminate */
247     return result;
248 }
249 
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)250 unsigned int run_ntlm_auth(const char *username,
251 			   const char *domain,
252 			   const char *full_username,
253 			   const char *plaintext_password,
254 			   const u_char *challenge,
255 			   size_t challenge_length,
256 			   const u_char *lm_response,
257 			   size_t lm_response_length,
258 			   const u_char *nt_response,
259 			   size_t nt_response_length,
260 			   u_char nt_key[16],
261 			   char **error_string)
262 {
263 
264 	pid_t forkret;
265         int child_in[2];
266         int child_out[2];
267 	int status;
268 
269 	int authenticated = NOT_AUTHENTICATED; /* not auth */
270 	int got_user_session_key = 0; /* not got key */
271 
272 	char buffer[1024];
273 
274 	FILE *pipe_in;
275 	FILE *pipe_out;
276 
277 	int i;
278 	char *challenge_hex;
279 	char *lm_hex_hash;
280 	char *nt_hex_hash;
281 
282 	/* First see if we have a program to run... */
283 	if (ntlm_auth == NULL)
284 		return NOT_AUTHENTICATED;
285 
286         /* Make first child */
287         if (pipe(child_out) == -1) {
288                 error("pipe creation failed for child OUT!");
289 		return NOT_AUTHENTICATED;
290         }
291 
292         if (pipe(child_in) == -1) {
293                 error("pipe creation failed for child IN!");
294 		return NOT_AUTHENTICATED;
295         }
296 
297         forkret = safe_fork(child_in[0], child_out[1], 2);
298         if (forkret == -1) {
299 		if (error_string) {
300 			*error_string = strdup("fork failed!");
301 		}
302 
303                 return NOT_AUTHENTICATED;
304         }
305 
306 	if (forkret == 0) {
307 		/* child process */
308 		close(child_out[0]);
309 		close(child_in[1]);
310 
311 		/* run winbind as the user that invoked pppd */
312 		setgid(getgid());
313 		setuid(getuid());
314 		execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);
315 		perror("pppd/winbind: could not exec /bin/sh");
316 		exit(1);
317 	}
318 
319         /* parent */
320 	close(child_out[1]);
321 	close(child_in[0]);
322 
323 	/* Need to write the User's info onto the pipe */
324 
325 	pipe_in = fdopen(child_in[1], "w");
326 
327 	pipe_out = fdopen(child_out[0], "r");
328 
329 	/* look for session key coming back */
330 
331 	if (username) {
332 		char *b64_username = base64_encode(username);
333 		fprintf(pipe_in, "Username:: %s\n", b64_username);
334 		free(b64_username);
335 	}
336 
337 	if (domain) {
338 		char *b64_domain = base64_encode(domain);
339 		fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
340 		free(b64_domain);
341 	}
342 
343 	if (full_username) {
344 		char *b64_full_username = base64_encode(full_username);
345 		fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
346 		free(b64_full_username);
347 	}
348 
349 	if (plaintext_password) {
350 		char *b64_plaintext_password = base64_encode(plaintext_password);
351 		fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
352 		free(b64_plaintext_password);
353 	}
354 
355 	if (challenge_length) {
356 		fprintf(pipe_in, "Request-User-Session-Key: yes\n");
357 
358 		challenge_hex = malloc(challenge_length*2+1);
359 
360 		for (i = 0; i < challenge_length; i++)
361 			sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
362 
363 		fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
364 		free(challenge_hex);
365 	}
366 
367 	if (lm_response_length) {
368 		lm_hex_hash = malloc(lm_response_length*2+1);
369 
370 		for (i = 0; i < lm_response_length; i++)
371 			sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
372 
373 		fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
374 		free(lm_hex_hash);
375 	}
376 
377 	if (nt_response_length) {
378 		nt_hex_hash = malloc(nt_response_length*2+1);
379 
380 		for (i = 0; i < nt_response_length; i++)
381 			sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
382 
383 		fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
384 		free(nt_hex_hash);
385 	}
386 
387 	fprintf(pipe_in, ".\n");
388 	fflush(pipe_in);
389 
390 	while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
391 		char *message, *parameter;
392 		if (buffer[strlen(buffer)-1] != '\n') {
393 			break;
394 		}
395 		buffer[strlen(buffer)-1] = '\0';
396 		message = buffer;
397 
398 		if (!(parameter = strstr(buffer, ": "))) {
399 			break;
400 		}
401 
402 		parameter[0] = '\0';
403 		parameter++;
404 		parameter[0] = '\0';
405 		parameter++;
406 
407 		if (strcmp(message, ".") == 0) {
408 			/* end of sequence */
409 			break;
410 		} else if (strcasecmp(message, "Authenticated") == 0) {
411 			if (strcasecmp(parameter, "Yes") == 0) {
412 				authenticated = AUTHENTICATED;
413 			} else {
414 				notice("Winbind has declined authentication for user!");
415 				authenticated = NOT_AUTHENTICATED;
416 			}
417 		} else if (strcasecmp(message, "User-session-key") == 0) {
418 			/* length is the number of characters to parse */
419 			if (nt_key) {
420 				if (strhex_to_str(nt_key, 32, parameter) == 16) {
421 					got_user_session_key = 1;
422 				} else {
423 					notice("NT session key for user was not 16 bytes!");
424 				}
425 			}
426 		} else if (strcasecmp(message, "Error") == 0) {
427 			authenticated = NOT_AUTHENTICATED;
428 			if (error_string)
429 				*error_string = strdup(parameter);
430 		} else if (strcasecmp(message, "Authentication-Error") == 0) {
431 			authenticated = NOT_AUTHENTICATED;
432 			if (error_string)
433 				*error_string = strdup(parameter);
434 		} else {
435 			notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
436 		}
437 	}
438 
439         /* parent */
440         if (close(child_out[0]) == -1) {
441                 notice("error closing pipe?!? for child OUT[0]");
442                 return NOT_AUTHENTICATED;
443         }
444 
445        /* parent */
446         if (close(child_in[1]) == -1) {
447                 notice("error closing pipe?!? for child IN[1]");
448                 return NOT_AUTHENTICATED;
449         }
450 
451 	while ((wait(&status) == -1) && errno == EINTR)
452                 ;
453 
454 	if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
455 		notice("Did not get user session key, despite being authenticated!");
456 		return NOT_AUTHENTICATED;
457 	}
458 	return authenticated;
459 }
460 
461 /**********************************************************************
462 * %FUNCTION: winbind_secret_check
463 * %ARGUMENTS:
464 *  None
465 * %RETURNS:
466 *  0 if we don't have an ntlm_auth program to run, otherwise 1.
467 * %DESCRIPTION:
468 * Tells pppd that we will try to authenticate the peer, and not to
469 * worry about looking in /etc/ppp/ *-secrets
470 ***********************************************************************/
471 static int
winbind_secret_check(void)472 winbind_secret_check(void)
473 {
474 	return ntlm_auth != NULL;
475 }
476 
477 /**********************************************************************
478 * %FUNCTION: winbind_pap_auth
479 * %ARGUMENTS:
480 *  user -- user-name of peer
481 *  passwd -- password supplied by peer
482 *  msgp -- Message which will be sent in PAP response
483 *  paddrs -- set to a list of possible peer IP addresses
484 *  popts -- set to a list of additional pppd options
485 * %RETURNS:
486 *  1 if we can authenticate, -1 if we cannot.
487 * %DESCRIPTION:
488 * Performs PAP authentication using WINBIND
489 ***********************************************************************/
490 static int
winbind_pap_auth(char * user,char * password,char ** msgp,struct wordlist ** paddrs,struct wordlist ** popts)491 winbind_pap_auth(char *user,
492 		char *password,
493 		char **msgp,
494 		struct wordlist **paddrs,
495 		struct wordlist **popts)
496 {
497 	if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
498 		return 1;
499 	}
500 	return -1;
501 }
502 
503 /**********************************************************************
504 * %FUNCTION: winbind_chap_auth
505 * %ARGUMENTS:
506 *  user -- user-name of peer
507 *  remmd -- hash received from peer
508 *  remmd_len -- length of remmd
509 *  cstate -- pppd's chap_state structure
510 * %RETURNS:
511 *  AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
512 * %DESCRIPTION:
513 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
514 ***********************************************************************/
515 
516 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)517 winbind_chap_verify(char *user, char *ourname, int id,
518 		    struct chap_digest_type *digest,
519 		    unsigned char *challenge,
520 		    unsigned char *response,
521 		    char *message, int message_space)
522 {
523 	int challenge_len, response_len;
524 	char domainname[256];
525 	char *domain;
526 	char *username;
527 	char *p;
528 	char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
529 
530 	/* The first byte of each of these strings contains their length */
531 	challenge_len = *challenge++;
532 	response_len = *response++;
533 
534 	/* remove domain from "domain\username" */
535 	if ((username = strrchr(user, '\\')) != NULL)
536 		++username;
537 	else
538 		username = user;
539 
540 	strlcpy(domainname, user, sizeof(domainname));
541 
542 	/* remove domain from "domain\username" */
543 	if ((p = strrchr(domainname, '\\')) != NULL) {
544 		*p = '\0';
545 		domain = domainname;
546 	} else {
547 		domain = NULL;
548 	}
549 
550 	/*  generate MD based on negotiated type */
551 	switch (digest->code) {
552 
553 	case CHAP_MICROSOFT:
554 	{
555 		char *error_string = NULL;
556 		u_char *nt_response = NULL;
557 		u_char *lm_response = NULL;
558 		int nt_response_size = 0;
559 		int lm_response_size = 0;
560 		MS_ChapResponse *rmd = (MS_ChapResponse *) response;
561 		u_char session_key[16];
562 
563 		if (response_len != MS_CHAP_RESPONSE_LEN)
564 			break;			/* not even the right length */
565 
566 		/* Determine which part of response to verify against */
567 		if (rmd->UseNT[0]) {
568 			nt_response = rmd->NTResp;
569 			nt_response_size = sizeof(rmd->NTResp);
570 		} else {
571 #ifdef MSLANMAN
572 			lm_response = rmd->LANManResp;
573 			lm_response_size = sizeof(rmd->LANManResp);
574 #else
575 			/* Should really propagate this into the error packet. */
576 			notice("Peer request for LANMAN auth not supported");
577 			return NOT_AUTHENTICATED;
578 #endif /* MSLANMAN */
579 		}
580 
581 		/* ship off to winbind, and check */
582 
583 		if (run_ntlm_auth(username,
584 				  domain,
585 				  NULL,
586 				  NULL,
587 				  challenge,
588 				  challenge_len,
589 				  lm_response,
590 				  lm_response ? lm_response_size: 0,
591 				  nt_response,
592 				  nt_response ? nt_response_size: 0,
593 				  session_key,
594 				  &error_string) == AUTHENTICATED) {
595 			mppe_set_keys(challenge, session_key);
596 			slprintf(message, message_space, "Access granted");
597 			return AUTHENTICATED;
598 
599 		} else {
600 			if (error_string) {
601 				notice(error_string);
602 				free(error_string);
603 			}
604 			slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
605 				 challenge_len, challenge);
606 			return NOT_AUTHENTICATED;
607 		}
608 		break;
609 	}
610 
611 	case CHAP_MICROSOFT_V2:
612 	{
613 		MS_Chap2Response *rmd = (MS_Chap2Response *) response;
614 		u_char Challenge[8];
615 		u_char session_key[MD4_SIGNATURE_SIZE];
616 		char *error_string = NULL;
617 
618 		if (response_len != MS_CHAP2_RESPONSE_LEN)
619 			break;			/* not even the right length */
620 
621 		ChallengeHash(rmd->PeerChallenge, challenge, user, Challenge);
622 
623 		/* ship off to winbind, and check */
624 
625 		if (run_ntlm_auth(username,
626 				  domain,
627 				  NULL,
628 				  NULL,
629 				  Challenge,
630 				  8,
631 				  NULL,
632 				  0,
633 				  rmd->NTResp,
634 				  sizeof(rmd->NTResp),
635 
636 				  session_key,
637 				  &error_string) == AUTHENTICATED) {
638 
639 			GenerateAuthenticatorResponse(session_key,
640 						      rmd->NTResp, rmd->PeerChallenge,
641 						      challenge, user,
642 						      saresponse);
643 			mppe_set_keys2(session_key, rmd->NTResp, MS_CHAP2_AUTHENTICATOR);
644 			if (rmd->Flags[0]) {
645 				slprintf(message, message_space, "S=%s", saresponse);
646 			} else {
647 				slprintf(message, message_space, "S=%s M=%s",
648 					 saresponse, "Access granted");
649 			}
650 			return AUTHENTICATED;
651 
652 		} else {
653 			if (error_string) {
654 				notice(error_string);
655 				slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
656 					 challenge_len, challenge, error_string);
657 				free(error_string);
658 			} else {
659 				slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
660 					 challenge_len, challenge, "Access denied");
661 			}
662 			return NOT_AUTHENTICATED;
663 		}
664 		break;
665 	}
666 
667 	default:
668 		error("WINBIND: Challenge type %u unsupported", digest->code);
669 	}
670 	return NOT_AUTHENTICATED;
671 }
672 
673 static int
winbind_allowed_address(u_int32_t addr)674 winbind_allowed_address(u_int32_t addr)
675 {
676 	ipcp_options *wo = &ipcp_wantoptions[0];
677 	if (wo->hisaddr !=0 && wo->hisaddr == addr) {
678 		return 1;
679 	}
680 	return -1;
681 }
682