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 ***********************************************************************/
38 #include <sys/types.h>
53 #include <pppd/pppd.h>
54 #include <pppd/options.h>
55 #include <pppd/chap.h>
56 #include <pppd/chap_ms.h>
57 #include <pppd/upap.h>
59 #include <pppd/ipcp.h>
60 #include <pppd/mppe.h>
61 #include <pppd/crypto.h>
65 #define NOT_AUTHENTICATED 0
66 #define AUTHENTICATED 1
68 static char *ntlm_auth = NULL;
70 static int set_ntlm_auth(char **argv)
76 ppp_option_error("ntlm_auth-helper argument must be full path");
81 novm("ntlm_auth-helper argument");
84 if (ntlm_auth != NULL)
90 static struct option Options[] = {
91 { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth,
92 "Path to ntlm_auth executable", OPT_PRIV },
96 static pap_check_hook_fn winbind_secret_check;
97 static pap_auth_hook_fn winbind_pap_auth;
98 static chap_verify_hook_fn winbind_chap_verify;
99 static int winbind_allowed_address(uint32_t addr);
101 char pppd_version[] = PPPD_VERSION;
103 /**********************************************************************
104 * %FUNCTION: plugin_init
110 * Initializes WINBIND plugin.
111 ***********************************************************************/
115 pap_check_hook = winbind_secret_check;
116 pap_auth_hook = winbind_pap_auth;
118 chap_check_hook = winbind_secret_check;
119 chap_verify_hook = winbind_chap_verify;
121 allowed_address_hook = winbind_allowed_address;
123 /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
124 chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
126 ppp_add_options(Options);
128 info("WINBIND plugin initialized.");
132 Routine to get hex characters and turn them into a 16 byte array.
133 the array can be variable length, and any non-hex-numeric
134 characters are skipped. "0xnn" or "0Xnn" is specially catered
137 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
142 Unix SMB/CIFS implementation.
143 Samba utility functions
145 Copyright (C) Andrew Tridgell 1992-2001
146 Copyright (C) Simo Sorce 2001-2002
147 Copyright (C) Martin Pool 2003
149 This program is free software; you can redistribute it and/or modify
150 it under the terms of the GNU General Public License as published by
151 the Free Software Foundation; either version 2 of the License, or
152 (at your option) any later version.
154 This program is distributed in the hope that it will be useful,
155 but WITHOUT ANY WARRANTY; without even the implied warranty of
156 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
157 GNU General Public License for more details.
159 You should have received a copy of the GNU General Public License
160 along with this program; if not, write to the Free Software
161 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
164 size_t strhex_to_str(unsigned char *p, size_t len, const char *strhex)
167 size_t num_chars = 0;
168 unsigned char lonybble, hinybble;
169 const char *hexchars = "0123456789ABCDEF";
170 char *p1 = NULL, *p2 = NULL;
172 for (i = 0; i < len && strhex[i] != 0; i++) {
173 if (strncmp(hexchars, "0x", 2) == 0) {
174 i++; /* skip two chars */
178 if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
181 i++; /* next hex digit */
183 if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
186 /* get the two nybbles */
187 hinybble = (p1 - hexchars);
188 lonybble = (p2 - hexchars);
190 p[num_chars] = (hinybble << 4) | lonybble;
199 static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
202 * Encode a base64 string into a malloc()ed string caller to free.
204 *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
206 char * base64_encode(const char *data)
209 size_t len = strlen(data);
210 size_t output_len = 4 * ((len + 2) / 3) + 2;
211 const unsigned char *ptr = (const unsigned char *) data;
212 char *result = malloc(output_len); /* get us plenty of space */
215 for (; len >= 3; len -= 3) {
216 bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
218 result[out_cnt++] = b64[bits >> 18];
219 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
220 result[out_cnt++] = b64[(bits >> 6) & 0x3f];
221 result[out_cnt++] = b64[bits & 0x3f];
227 result[out_cnt++] = b64[bits >> 18];
228 result[out_cnt++] = b64[(bits >> 12) & 0x3f];
229 result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '=';
230 result[out_cnt++] = '=';
233 result[out_cnt] = '\0'; /* terminate */
237 unsigned int run_ntlm_auth(const char *username,
239 const char *full_username,
240 const char *plaintext_password,
241 const u_char *challenge,
242 size_t challenge_length,
243 const u_char *lm_response,
244 size_t lm_response_length,
245 const u_char *nt_response,
246 size_t nt_response_length,
256 int authenticated = NOT_AUTHENTICATED; /* not auth */
257 int got_user_session_key = 0; /* not got key */
269 /* First see if we have a program to run... */
270 if (ntlm_auth == NULL)
271 return NOT_AUTHENTICATED;
273 /* Make first child */
274 if (pipe(child_out) == -1) {
275 error("pipe creation failed for child OUT!");
276 return NOT_AUTHENTICATED;
279 if (pipe(child_in) == -1) {
280 error("pipe creation failed for child IN!");
281 return NOT_AUTHENTICATED;
284 forkret = ppp_safe_fork(child_in[0], child_out[1], 2);
287 *error_string = strdup("fork failed!");
290 return NOT_AUTHENTICATED;
301 /* run winbind as the user that invoked pppd */
303 if (setgid(gid) == -1 || getgid() != gid) {
304 fatal("pppd/winbind: could not setgid to %d: %m", gid);
307 if (setuid(uid) == -1 || getuid() != uid) {
308 fatal("pppd/winbind: could not setuid to %d: %m", uid);
310 execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);
311 fatal("pppd/winbind: could not exec /bin/sh: %m");
318 /* Need to write the User's info onto the pipe */
320 pipe_in = fdopen(child_in[1], "w");
322 pipe_out = fdopen(child_out[0], "r");
324 /* look for session key coming back */
327 char *b64_username = base64_encode(username);
328 fprintf(pipe_in, "Username:: %s\n", b64_username);
333 char *b64_domain = base64_encode(domain);
334 fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
339 char *b64_full_username = base64_encode(full_username);
340 fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
341 free(b64_full_username);
344 if (plaintext_password) {
345 char *b64_plaintext_password = base64_encode(plaintext_password);
346 fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
347 free(b64_plaintext_password);
350 if (challenge_length) {
351 fprintf(pipe_in, "Request-User-Session-Key: yes\n");
353 challenge_hex = malloc(challenge_length*2+1);
355 for (i = 0; i < challenge_length; i++)
356 sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
358 fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
362 if (lm_response_length) {
363 lm_hex_hash = malloc(lm_response_length*2+1);
365 for (i = 0; i < lm_response_length; i++)
366 sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
368 fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
372 if (nt_response_length) {
373 nt_hex_hash = malloc(nt_response_length*2+1);
375 for (i = 0; i < nt_response_length; i++)
376 sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
378 fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
382 fprintf(pipe_in, ".\n");
385 while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
386 char *message, *parameter;
387 if (buffer[strlen(buffer)-1] != '\n') {
390 buffer[strlen(buffer)-1] = '\0';
393 if (!(parameter = strstr(buffer, ": "))) {
402 if (strcmp(message, ".") == 0) {
403 /* end of sequence */
405 } else if (strcasecmp(message, "Authenticated") == 0) {
406 if (strcasecmp(parameter, "Yes") == 0) {
407 authenticated = AUTHENTICATED;
409 notice("Winbind has declined authentication for user!");
410 authenticated = NOT_AUTHENTICATED;
412 } else if (strcasecmp(message, "User-session-key") == 0) {
413 /* length is the number of characters to parse */
415 if (strhex_to_str(nt_key, 32, parameter) == 16) {
416 got_user_session_key = 1;
418 notice("NT session key for user was not 16 bytes!");
421 } else if (strcasecmp(message, "Error") == 0) {
422 authenticated = NOT_AUTHENTICATED;
424 *error_string = strdup(parameter);
425 } else if (strcasecmp(message, "Authentication-Error") == 0) {
426 authenticated = NOT_AUTHENTICATED;
428 *error_string = strdup(parameter);
430 notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
435 if (close(child_out[0]) == -1) {
437 notice("error closing pipe?!? for child OUT[0]");
438 return NOT_AUTHENTICATED;
442 if (close(child_in[1]) == -1) {
443 notice("error closing pipe?!? for child IN[1]");
444 return NOT_AUTHENTICATED;
447 while ((wait(&status) == -1) && errno == EINTR && !ppp_signaled(SIGTERM))
450 if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
451 notice("Did not get user session key, despite being authenticated!");
452 return NOT_AUTHENTICATED;
454 return authenticated;
457 /**********************************************************************
458 * %FUNCTION: winbind_secret_check
462 * 0 if we don't have an ntlm_auth program to run, otherwise 1.
464 * Tells pppd that we will try to authenticate the peer, and not to
465 * worry about looking in /etc/ppp/ *-secrets
466 ***********************************************************************/
468 winbind_secret_check(void)
470 return ntlm_auth != NULL;
473 /**********************************************************************
474 * %FUNCTION: winbind_pap_auth
476 * user -- user-name of peer
477 * passwd -- password supplied by peer
478 * msgp -- Message which will be sent in PAP response
479 * paddrs -- set to a list of possible peer IP addresses
480 * popts -- set to a list of additional pppd options
482 * 1 if we can authenticate, -1 if we cannot.
484 * Performs PAP authentication using WINBIND
485 ***********************************************************************/
487 winbind_pap_auth(char *user,
490 struct wordlist **paddrs,
491 struct wordlist **popts)
493 if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
499 /**********************************************************************
500 * %FUNCTION: winbind_chap_auth
502 * user -- user-name of peer
503 * remmd -- hash received from peer
504 * remmd_len -- length of remmd
505 * cstate -- pppd's chap_state structure
507 * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
509 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
510 ***********************************************************************/
513 winbind_chap_verify(char *user, char *ourname, int id,
514 struct chap_digest_type *digest,
515 unsigned char *challenge,
516 unsigned char *response,
517 char *message, int message_space)
519 int challenge_len, response_len;
520 char domainname[256];
522 const char *username;
524 unsigned char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
526 /* The first byte of each of these strings contains their length */
527 challenge_len = *challenge++;
528 response_len = *response++;
530 /* remove domain from "domain\username" */
531 if ((username = strrchr(user, '\\')) != NULL)
536 strlcpy(domainname, user, sizeof(domainname));
538 /* remove domain from "domain\username" */
539 if ((p = strrchr(domainname, '\\')) != NULL) {
546 /* generate MD based on negotiated type */
547 switch (digest->code) {
551 char *error_string = NULL;
552 u_char *nt_response = NULL;
553 u_char *lm_response = NULL;
554 int nt_response_size = 0;
555 int lm_response_size = 0;
556 u_char session_key[MD4_DIGEST_LENGTH];
558 if (response_len != MS_CHAP_RESPONSE_LEN)
559 break; /* not even the right length */
561 /* Determine which part of response to verify against */
562 if (response[MS_CHAP_USENT]) {
563 nt_response = &response[MS_CHAP_NTRESP];
564 nt_response_size = MS_CHAP_NTRESP_LEN;
566 #ifdef PPP_WITH_MSLANMAN
567 lm_response = &response[MS_CHAP_LANMANRESP];
568 lm_response_size = MS_CHAP_LANMANRESP_LEN;
570 /* Should really propagate this into the error packet. */
571 notice("Peer request for LANMAN auth not supported");
572 return NOT_AUTHENTICATED;
573 #endif /* PPP_WITH_MSLANMAN */
576 /* ship off to winbind, and check */
578 if (run_ntlm_auth(username,
582 challenge, challenge_len,
583 lm_response, lm_response_size,
584 nt_response, nt_response_size,
586 &error_string) == AUTHENTICATED) {
588 mppe_set_chapv1(challenge, session_key);
590 slprintf(message, message_space, "Access granted");
591 return AUTHENTICATED;
595 notice(error_string);
598 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
599 challenge_len, challenge);
600 return NOT_AUTHENTICATED;
605 case CHAP_MICROSOFT_V2:
608 u_char session_key[MD4_DIGEST_LENGTH];
609 char *error_string = NULL;
611 if (response_len != MS_CHAP2_RESPONSE_LEN)
612 break; /* not even the right length */
614 ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
617 /* ship off to winbind, and check */
619 if (run_ntlm_auth(username,
625 &response[MS_CHAP2_NTRESP],
628 &error_string) == AUTHENTICATED) {
630 GenerateAuthenticatorResponse(session_key,
631 &response[MS_CHAP2_NTRESP],
632 &response[MS_CHAP2_PEER_CHALLENGE],
633 challenge, user, saresponse);
635 mppe_set_chapv2(session_key, &response[MS_CHAP2_NTRESP],
636 MS_CHAP2_AUTHENTICATOR);
638 if (response[MS_CHAP2_FLAGS]) {
639 slprintf(message, message_space, "S=%s", saresponse);
641 slprintf(message, message_space, "S=%s M=%s",
642 saresponse, "Access granted");
644 return AUTHENTICATED;
648 notice(error_string);
649 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
650 challenge_len, challenge, error_string);
653 slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
654 challenge_len, challenge, "Access denied");
656 return NOT_AUTHENTICATED;
662 error("WINBIND: Challenge type %u unsupported", digest->code);
664 return NOT_AUTHENTICATED;
668 winbind_allowed_address(uint32_t addr)
670 ipcp_options *wo = &ipcp_wantoptions[0];
671 if (wo->hisaddr !=0 && wo->hisaddr == addr) {