1 /***********************************************************************
5 * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
6 * authentication using WINBIND to contact a NT-style PDC.
8 * Based on the structure of the radius module.
10 * Copyright (C) 2003 Andrew Bartlet <abartlet@samba.org>
12 * Copyright 1999 Paul Mackerras, Alan Curry.
13 * (pipe read code from passpromt.c)
15 * Copyright (C) 2002 Roaring Penguin Software Inc.
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>
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.
26 * MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
27 * modification from Frank Cusack, <frank@google.com>.
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>
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.
35 ***********************************************************************/
46 #include <sys/types.h>
59 #define NOT_AUTHENTICATED 0
60 #define AUTHENTICATED 1
62 static char *ntlm_auth = NULL;
64 static option_t Options[] = {
65 { "ntlm_auth-helper", o_string, &ntlm_auth },
70 winbind_secret_check(void);
72 static int winbind_pap_auth(char *user,
75 struct wordlist **paddrs,
76 struct wordlist **popts);
77 static int winbind_chap_verify(char *user, char *ourname, int id,
78 struct chap_digest_type *digest,
79 unsigned char *challenge,
80 unsigned char *response,
81 char *message, int message_space);
82 static int winbind_allowed_address(u_int32_t addr);
84 char pppd_version[] = VERSION;
86 /**********************************************************************
87 * %FUNCTION: plugin_init
93 * Initializes WINBIND plugin.
94 ***********************************************************************/
98 pap_check_hook = winbind_secret_check;
99 pap_auth_hook = winbind_pap_auth;
101 chap_check_hook = winbind_secret_check;
102 chap_verify_hook = winbind_chap_verify;
104 allowed_address_hook = winbind_allowed_address;
106 /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
107 chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
109 add_options(Options);
111 info("WINBIND plugin initialized.");
115 Routine to get hex characters and turn them into a 16 byte array.
116 the array can be variable length, and any non-hex-numeric
117 characters are skipped. "0xnn" or "0Xnn" is specially catered
120 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
125 Unix SMB/CIFS implementation.
126 Samba utility functions
128 Copyright (C) Andrew Tridgell 1992-2001
129 Copyright (C) Simo Sorce 2001-2002
130 Copyright (C) Martin Pool 2003
132 This program is free software; you can redistribute it and/or modify
133 it under the terms of the GNU General Public License as published by
134 the Free Software Foundation; either version 2 of the License, or
135 (at your option) any later version.
137 This program is distributed in the hope that it will be useful,
138 but WITHOUT ANY WARRANTY; without even the implied warranty of
139 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
140 GNU General Public License for more details.
142 You should have received a copy of the GNU General Public License
143 along with this program; if not, write to the Free Software
144 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
147 size_t strhex_to_str(char *p, size_t len, const char *strhex)
150 size_t num_chars = 0;
151 unsigned char lonybble, hinybble;
152 const char *hexchars = "0123456789ABCDEF";
153 char *p1 = NULL, *p2 = NULL;
155 for (i = 0; i < len && strhex[i] != 0; i++) {
156 if (strncmp(hexchars, "0x", 2) == 0) {
157 i++; /* skip two chars */
161 if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
164 i++; /* next hex digit */
166 if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
169 /* get the two nybbles */
170 hinybble = (p1 - hexchars);
171 lonybble = (p2 - hexchars);
173 p[num_chars] = (hinybble << 4) | lonybble;
182 static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
185 * Encode a base64 string into a malloc()ed string caller to free.
187 *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
189 char * base64_encode(const char *data)
194 size_t len = strlen(data);
195 size_t output_len = strlen(data) * 2;
196 char *result = malloc(output_len); /* get us plenty of space */
198 while (len-- && out_cnt < (output_len) - 5) {
199 int c = (unsigned char) *(data++);
202 if (char_count == 3) {
203 result[out_cnt++] = b64[bits >> 18];
204 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
205 result[out_cnt++] = b64[(bits >> 6) & 0x3f];
206 result[out_cnt++] = b64[bits & 0x3f];
213 if (char_count != 0) {
214 bits <<= 16 - (8 * char_count);
215 result[out_cnt++] = b64[bits >> 18];
216 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
217 if (char_count == 1) {
218 result[out_cnt++] = '=';
219 result[out_cnt++] = '=';
221 result[out_cnt++] = b64[(bits >> 6) & 0x3f];
222 result[out_cnt++] = '=';
225 result[out_cnt] = '\0'; /* terminate */
229 unsigned int run_ntlm_auth(const char *username,
231 const char *full_username,
232 const char *plaintext_password,
233 const u_char *challenge,
234 size_t challenge_length,
235 const u_char *lm_response,
236 size_t lm_response_length,
237 const u_char *nt_response,
238 size_t nt_response_length,
248 int authenticated = NOT_AUTHENTICATED; /* not auth */
249 int got_user_session_key = 0; /* not got key */
261 /* Make first child */
262 if (pipe(child_out) == -1) {
263 perror("pipe creation failed for child OUT!");
266 if (pipe(child_in) == -1) {
267 perror("pipe creation failed for child IN!");
272 perror("fork failed!");
274 *error_string = strdup("fork failed!");
277 return NOT_AUTHENTICATED;
278 } else if (forkret == 0) {
279 /* child - pipe out */
280 if (close(child_out[0]) == -1) {
281 perror("error closing pipe?!? for child OUT[READFD]");
284 if (dup2(child_out[1], 1) == -1) {
285 perror("(child) dup2 of fdout onto STDOUT failed!");
288 /* Close extra copies */
289 if (close(child_out[1]) == -1) {
290 perror("error closing pipe?!? for child OUT[WRITEFD]");
294 /* child - pipe in */
295 if (close(child_in[1]) == -1) {
296 perror("error closing pipe?!? for child IN[WRITEFD]");
299 if (dup2(child_in[0], 0) == -1) {
300 perror("(child) dup2 of fdin onto STDIN failed!");
303 /* Close extra copies */
304 if (close(child_in[0]) == -1) {
305 perror("error closing pipe?!? for child IN[READFD]");
309 execl("/bin/sh","sh","-c", ntlm_auth,NULL);
317 if (close(child_out[1]) == -1) {
318 notice("error closing pipe?!? for child OUT[1]");
319 return NOT_AUTHENTICATED;
322 if (close(child_in[0]) == -1) {
323 notice("error closing pipe?!? for child OUT[1]");
324 return NOT_AUTHENTICATED;
327 /* Need to write the User's info onto the pipe */
329 pipe_in = fdopen(child_in[1], "w");
331 pipe_out = fdopen(child_out[0], "r");
333 /* look for session key coming back */
336 char *b64_username = base64_encode(username);
337 fprintf(pipe_in, "Username:: %s\n", b64_username);
342 char *b64_domain = base64_encode(domain);
343 fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
348 char *b64_full_username = base64_encode(full_username);
349 fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
350 free(b64_full_username);
353 if (plaintext_password) {
354 char *b64_plaintext_password = base64_encode(plaintext_password);
355 fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
356 free(b64_plaintext_password);
359 if (challenge_length) {
360 fprintf(pipe_in, "Request-User-Session-Key: yes\n");
362 challenge_hex = malloc(challenge_length*2+1);
364 for (i = 0; i < challenge_length; i++)
365 sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
367 fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
371 if (lm_response_length) {
372 lm_hex_hash = malloc(lm_response_length*2+1);
374 for (i = 0; i < lm_response_length; i++)
375 sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
377 fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
381 if (nt_response_length) {
382 nt_hex_hash = malloc(nt_response_length*2+1);
384 for (i = 0; i < nt_response_length; i++)
385 sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
387 fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
391 fprintf(pipe_in, ".\n");
394 while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
395 char *message, *parameter;
396 if (buffer[strlen(buffer)-1] != '\n') {
399 buffer[strlen(buffer)-1] = '\0';
402 if (!(parameter = strstr(buffer, ": "))) {
411 if (strcmp(message, ".") == 0) {
412 /* end of sequence */
414 } else if (strcasecmp(message, "Authenticated") == 0) {
415 if (strcasecmp(parameter, "Yes") == 0) {
416 authenticated = AUTHENTICATED;
418 notice("Winbind has declined authentication for user!");
419 authenticated = NOT_AUTHENTICATED;
421 } else if (strcasecmp(message, "User-session-key") == 0) {
422 /* length is the number of characters to parse */
424 if (strhex_to_str(nt_key, 32, parameter) == 16) {
425 got_user_session_key = 1;
427 notice("NT session key for user was not 16 bytes!");
430 } else if (strcasecmp(message, "Error") == 0) {
431 authenticated = NOT_AUTHENTICATED;
433 *error_string = strdup(parameter);
434 } else if (strcasecmp(message, "Authentication-Error") == 0) {
435 authenticated = NOT_AUTHENTICATED;
437 *error_string = strdup(parameter);
439 notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
444 if (close(child_out[0]) == -1) {
445 notice("error closing pipe?!? for child OUT[0]");
446 return NOT_AUTHENTICATED;
450 if (close(child_in[1]) == -1) {
451 notice("error closing pipe?!? for child IN[1]");
452 return NOT_AUTHENTICATED;
455 while ((wait(&status) == -1) && errno == EINTR)
458 if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
459 notice("Did not get user session key, despite being authenticated!");
460 return NOT_AUTHENTICATED;
462 return authenticated;
465 /**********************************************************************
466 * %FUNCTION: winbind_secret_check
470 * 1 -- we are ALWAYS willing to supply a secret. :-)
472 * Tells pppd that we will try to authenticate the peer, and not to
473 * worry about looking in /etc/ppp/ *-secrets
474 ***********************************************************************/
476 winbind_secret_check(void)
481 /**********************************************************************
482 * %FUNCTION: winbind_pap_auth
484 * user -- user-name of peer
485 * passwd -- password supplied by peer
486 * msgp -- Message which will be sent in PAP response
487 * paddrs -- set to a list of possible peer IP addresses
488 * popts -- set to a list of additional pppd options
490 * 1 if we can authenticate, -1 if we cannot.
492 * Performs PAP authentication using WINBIND
493 ***********************************************************************/
495 winbind_pap_auth(char *user,
498 struct wordlist **paddrs,
499 struct wordlist **popts)
501 if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
507 /**********************************************************************
508 * %FUNCTION: winbind_chap_auth
510 * user -- user-name of peer
511 * remmd -- hash received from peer
512 * remmd_len -- length of remmd
513 * cstate -- pppd's chap_state structure
515 * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
517 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
518 ***********************************************************************/
521 winbind_chap_verify(char *user, char *ourname, int id,
522 struct chap_digest_type *digest,
523 unsigned char *challenge,
524 unsigned char *response,
525 char *message, int message_space)
527 int challenge_len, response_len;
528 char domainname[256];
532 char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
534 /* The first byte of each of these strings contains their length */
535 challenge_len = *challenge++;
536 response_len = *response++;
538 /* remove domain from "domain\username" */
539 if ((username = strrchr(user, '\\')) != NULL)
544 strlcpy(domainname, user, sizeof(domainname));
546 /* remove domain from "domain\username" */
547 if ((p = strrchr(domainname, '\\')) != NULL) {
554 /* generate MD based on negotiated type */
555 switch (digest->code) {
559 char *error_string = NULL;
560 u_char *nt_response = NULL;
561 u_char *lm_response = NULL;
562 int nt_response_size = 0;
563 int lm_response_size = 0;
564 MS_ChapResponse *rmd = (MS_ChapResponse *) response;
565 u_char session_key[16];
567 if (response_len != MS_CHAP_RESPONSE_LEN)
568 break; /* not even the right length */
570 /* Determine which part of response to verify against */
572 nt_response = rmd->NTResp;
573 nt_response_size = sizeof(rmd->NTResp);
576 lm_response = rmd->LANManResp;
577 lm_response_size = sizeof(rmd->LANManResp);
579 /* Should really propagate this into the error packet. */
580 notice("Peer request for LANMAN auth not supported");
581 return NOT_AUTHENTICATED;
582 #endif /* MSLANMAN */
585 /* ship off to winbind, and check */
587 if (run_ntlm_auth(username,
594 lm_response ? lm_response_size: 0,
596 nt_response ? nt_response_size: 0,
598 &error_string) == AUTHENTICATED) {
599 mppe_set_keys(challenge, session_key);
600 slprintf(message, message_space, "Access granted");
601 return AUTHENTICATED;
605 notice(error_string);
608 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
609 challenge_len, challenge);
610 return NOT_AUTHENTICATED;
615 case CHAP_MICROSOFT_V2:
617 MS_Chap2Response *rmd = (MS_Chap2Response *) response;
619 u_char session_key[MD4_SIGNATURE_SIZE];
620 char *error_string = NULL;
622 if (response_len != MS_CHAP2_RESPONSE_LEN)
623 break; /* not even the right length */
625 ChallengeHash(rmd->PeerChallenge, challenge, user, Challenge);
627 /* ship off to winbind, and check */
629 if (run_ntlm_auth(username,
641 &error_string) == AUTHENTICATED) {
643 GenerateAuthenticatorResponse(session_key,
644 rmd->NTResp, rmd->PeerChallenge,
647 mppe_set_keys2(session_key, rmd->NTResp, MS_CHAP2_AUTHENTICATOR);
649 slprintf(message, message_space, "S=%s", saresponse);
651 slprintf(message, message_space, "S=%s M=%s",
652 saresponse, "Access granted");
654 return AUTHENTICATED;
658 notice(error_string);
659 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
660 challenge_len, challenge, error_string);
663 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
664 challenge_len, challenge, "Access denied");
666 return NOT_AUTHENTICATED;
672 error("WINBIND: Challenge type %u unsupported", digest->code);
674 return NOT_AUTHENTICATED;
678 winbind_allowed_address(u_int32_t addr)
680 ipcp_options *wo = &ipcp_wantoptions[0];
681 if (wo->hisaddr !=0 && wo->hisaddr == addr) {