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 int set_ntlm_auth(char **argv
)
70 option_error("ntlm_auth-helper argument must be full path");
75 novm("ntlm_auth-helper argument");
78 if (ntlm_auth
!= NULL
)
84 static option_t Options
[] = {
85 { "ntlm_auth-helper", o_special
, (void *) &set_ntlm_auth
,
86 "Path to ntlm_auth executable", OPT_PRIV
},
91 winbind_secret_check(void);
93 static int winbind_pap_auth(char *user
,
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
);
105 char pppd_version
[] = VERSION
;
107 /**********************************************************************
108 * %FUNCTION: plugin_init
114 * Initializes WINBIND plugin.
115 ***********************************************************************/
119 pap_check_hook
= winbind_secret_check
;
120 pap_auth_hook
= winbind_pap_auth
;
122 chap_check_hook
= winbind_secret_check
;
123 chap_verify_hook
= winbind_chap_verify
;
125 allowed_address_hook
= winbind_allowed_address
;
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
);
130 add_options(Options
);
132 info("WINBIND plugin initialized.");
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
141 valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
146 Unix SMB/CIFS implementation.
147 Samba utility functions
149 Copyright (C) Andrew Tridgell 1992-2001
150 Copyright (C) Simo Sorce 2001-2002
151 Copyright (C) Martin Pool 2003
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.
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.
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.
168 size_t strhex_to_str(char *p
, size_t len
, const char *strhex
)
171 size_t num_chars
= 0;
172 unsigned char lonybble
, hinybble
;
173 const char *hexchars
= "0123456789ABCDEF";
174 char *p1
= NULL
, *p2
= NULL
;
176 for (i
= 0; i
< len
&& strhex
[i
] != 0; i
++) {
177 if (strncmp(hexchars
, "0x", 2) == 0) {
178 i
++; /* skip two chars */
182 if (!(p1
= strchr(hexchars
, toupper(strhex
[i
]))))
185 i
++; /* next hex digit */
187 if (!(p2
= strchr(hexchars
, toupper(strhex
[i
]))))
190 /* get the two nybbles */
191 hinybble
= (p1
- hexchars
);
192 lonybble
= (p2
- hexchars
);
194 p
[num_chars
] = (hinybble
<< 4) | lonybble
;
203 static const char *b64
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
206 * Encode a base64 string into a malloc()ed string caller to free.
208 *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
210 char * base64_encode(const char *data
)
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 */
219 for (; len
>= 3; len
-= 3) {
220 bits
= (ptr
[0] << 16) + (ptr
[1] << 8) + ptr
[2];
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];
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
++] = '=';
237 result
[out_cnt
] = '\0'; /* terminate */
241 unsigned int run_ntlm_auth(const char *username
,
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
,
260 int authenticated
= NOT_AUTHENTICATED
; /* not auth */
261 int got_user_session_key
= 0; /* not got key */
273 /* First see if we have a program to run... */
274 if (ntlm_auth
== NULL
)
275 return NOT_AUTHENTICATED
;
277 /* Make first child */
278 if (pipe(child_out
) == -1) {
279 error("pipe creation failed for child OUT!");
280 return NOT_AUTHENTICATED
;
283 if (pipe(child_in
) == -1) {
284 error("pipe creation failed for child IN!");
285 return NOT_AUTHENTICATED
;
288 forkret
= safe_fork(child_in
[0], child_out
[1], 2);
291 *error_string
= strdup("fork failed!");
294 return NOT_AUTHENTICATED
;
304 /* run winbind as the user that invoked pppd */
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");
317 /* Need to write the User's info onto the pipe */
319 pipe_in
= fdopen(child_in
[1], "w");
321 pipe_out
= fdopen(child_out
[0], "r");
323 /* look for session key coming back */
326 char *b64_username
= base64_encode(username
);
327 fprintf(pipe_in
, "Username:: %s\n", b64_username
);
332 char *b64_domain
= base64_encode(domain
);
333 fprintf(pipe_in
, "NT-Domain:: %s\n", b64_domain
);
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
);
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
);
349 if (challenge_length
) {
350 fprintf(pipe_in
, "Request-User-Session-Key: yes\n");
352 challenge_hex
= malloc(challenge_length
*2+1);
354 for (i
= 0; i
< challenge_length
; i
++)
355 sprintf(challenge_hex
+ i
* 2, "%02X", challenge
[i
]);
357 fprintf(pipe_in
, "LANMAN-Challenge: %s\n", challenge_hex
);
361 if (lm_response_length
) {
362 lm_hex_hash
= malloc(lm_response_length
*2+1);
364 for (i
= 0; i
< lm_response_length
; i
++)
365 sprintf(lm_hex_hash
+ i
* 2, "%02X", lm_response
[i
]);
367 fprintf(pipe_in
, "LANMAN-response: %s\n", lm_hex_hash
);
371 if (nt_response_length
) {
372 nt_hex_hash
= malloc(nt_response_length
*2+1);
374 for (i
= 0; i
< nt_response_length
; i
++)
375 sprintf(nt_hex_hash
+ i
* 2, "%02X", nt_response
[i
]);
377 fprintf(pipe_in
, "NT-response: %s\n", nt_hex_hash
);
381 fprintf(pipe_in
, ".\n");
384 while (fgets(buffer
, sizeof(buffer
)-1, pipe_out
) != NULL
) {
385 char *message
, *parameter
;
386 if (buffer
[strlen(buffer
)-1] != '\n') {
389 buffer
[strlen(buffer
)-1] = '\0';
392 if (!(parameter
= strstr(buffer
, ": "))) {
401 if (strcmp(message
, ".") == 0) {
402 /* end of sequence */
404 } else if (strcasecmp(message
, "Authenticated") == 0) {
405 if (strcasecmp(parameter
, "Yes") == 0) {
406 authenticated
= AUTHENTICATED
;
408 notice("Winbind has declined authentication for user!");
409 authenticated
= NOT_AUTHENTICATED
;
411 } else if (strcasecmp(message
, "User-session-key") == 0) {
412 /* length is the number of characters to parse */
414 if (strhex_to_str(nt_key
, 32, parameter
) == 16) {
415 got_user_session_key
= 1;
417 notice("NT session key for user was not 16 bytes!");
420 } else if (strcasecmp(message
, "Error") == 0) {
421 authenticated
= NOT_AUTHENTICATED
;
423 *error_string
= strdup(parameter
);
424 } else if (strcasecmp(message
, "Authentication-Error") == 0) {
425 authenticated
= NOT_AUTHENTICATED
;
427 *error_string
= strdup(parameter
);
429 notice("unrecognised input from ntlm_auth helper - %s: %s", message
, parameter
);
434 if (close(child_out
[0]) == -1) {
435 notice("error closing pipe?!? for child OUT[0]");
436 return NOT_AUTHENTICATED
;
440 if (close(child_in
[1]) == -1) {
441 notice("error closing pipe?!? for child IN[1]");
442 return NOT_AUTHENTICATED
;
445 while ((wait(&status
) == -1) && errno
== EINTR
)
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
;
452 return authenticated
;
455 /**********************************************************************
456 * %FUNCTION: winbind_secret_check
460 * 0 if we don't have an ntlm_auth program to run, otherwise 1.
462 * Tells pppd that we will try to authenticate the peer, and not to
463 * worry about looking in /etc/ppp/ *-secrets
464 ***********************************************************************/
466 winbind_secret_check(void)
468 return ntlm_auth
!= NULL
;
471 /**********************************************************************
472 * %FUNCTION: winbind_pap_auth
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
480 * 1 if we can authenticate, -1 if we cannot.
482 * Performs PAP authentication using WINBIND
483 ***********************************************************************/
485 winbind_pap_auth(char *user
,
488 struct wordlist
**paddrs
,
489 struct wordlist
**popts
)
491 if (run_ntlm_auth(NULL
, NULL
, user
, password
, NULL
, 0, NULL
, 0, NULL
, 0, NULL
, msgp
) == AUTHENTICATED
) {
497 /**********************************************************************
498 * %FUNCTION: winbind_chap_auth
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
505 * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
507 * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
508 ***********************************************************************/
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
)
517 int challenge_len
, response_len
;
518 char domainname
[256];
522 char saresponse
[MS_AUTH_RESPONSE_LENGTH
+1];
524 /* The first byte of each of these strings contains their length */
525 challenge_len
= *challenge
++;
526 response_len
= *response
++;
528 /* remove domain from "domain\username" */
529 if ((username
= strrchr(user
, '\\')) != NULL
)
534 strlcpy(domainname
, user
, sizeof(domainname
));
536 /* remove domain from "domain\username" */
537 if ((p
= strrchr(domainname
, '\\')) != NULL
) {
544 /* generate MD based on negotiated type */
545 switch (digest
->code
) {
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];
556 if (response_len
!= MS_CHAP_RESPONSE_LEN
)
557 break; /* not even the right length */
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
;
565 lm_response
= &response
[MS_CHAP_LANMANRESP
];
566 lm_response_size
= MS_CHAP_LANMANRESP_LEN
;
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 */
574 /* ship off to winbind, and check */
576 if (run_ntlm_auth(username
,
580 challenge
, challenge_len
,
581 lm_response
, lm_response_size
,
582 nt_response
, nt_response_size
,
584 &error_string
) == AUTHENTICATED
) {
585 mppe_set_keys(challenge
, session_key
);
586 slprintf(message
, message_space
, "Access granted");
587 return AUTHENTICATED
;
591 notice(error_string
);
594 slprintf(message
, message_space
, "E=691 R=1 C=%0.*B V=0",
595 challenge_len
, challenge
);
596 return NOT_AUTHENTICATED
;
601 case CHAP_MICROSOFT_V2
:
604 u_char session_key
[MD4_SIGNATURE_SIZE
];
605 char *error_string
= NULL
;
607 if (response_len
!= MS_CHAP2_RESPONSE_LEN
)
608 break; /* not even the right length */
610 ChallengeHash(&response
[MS_CHAP2_PEER_CHALLENGE
], challenge
,
613 /* ship off to winbind, and check */
615 if (run_ntlm_auth(username
,
621 &response
[MS_CHAP2_NTRESP
],
624 &error_string
) == AUTHENTICATED
) {
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
);
635 slprintf(message
, message_space
, "S=%s M=%s",
636 saresponse
, "Access granted");
638 return AUTHENTICATED
;
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
);
647 slprintf(message
, message_space
, "E=691 R=1 C=%0.*B V=0 M=%s",
648 challenge_len
, challenge
, "Access denied");
650 return NOT_AUTHENTICATED
;
656 error("WINBIND: Challenge type %u unsupported", digest
->code
);
658 return NOT_AUTHENTICATED
;
662 winbind_allowed_address(u_int32_t addr
)
664 ipcp_options
*wo
= &ipcp_wantoptions
[0];
665 if (wo
->hisaddr
!=0 && wo
->hisaddr
== addr
) {