123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- /***********************************************************************
- *
- * winbind.c
- *
- * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
- * authentication using WINBIND to contact a NT-style PDC.
- *
- * Based on the structure of the radius module.
- *
- * Copyright (C) 2003 Andrew Bartlet <abartlet@samba.org>
- *
- * Copyright 1999 Paul Mackerras, Alan Curry.
- * (pipe read code from passpromt.c)
- *
- * Copyright (C) 2002 Roaring Penguin Software Inc.
- *
- * Based on a patch for ipppd, which is:
- * Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
- * Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
- * Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
- *
- * Uses radiusclient library, which is:
- * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
- * Copyright (C) 2002 Roaring Penguin Software Inc.
- *
- * MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
- * modification from Frank Cusack, <frank@google.com>.
- *
- * Updated on 2003-12-12 to support updated PPP plugin API from latest CVS
- * Copyright (C) 2003, Sean E. Millichamp <sean at bruenor dot org>
- *
- * This plugin may be distributed according to the terms of the GNU
- * General Public License, version 2 or (at your option) any later version.
- *
- ***********************************************************************/
- #include "pppd.h"
- #include "chap-new.h"
- #include "chap_ms.h"
- #ifdef MPPE
- #include "md5.h"
- #endif
- #include "fsm.h"
- #include "ipcp.h"
- #include <syslog.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/time.h>
- #include <sys/wait.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <ctype.h>
- #define BUF_LEN 1024
- #define NOT_AUTHENTICATED 0
- #define AUTHENTICATED 1
- static char *ntlm_auth = NULL;
- static int set_ntlm_auth(char **argv)
- {
- char *p;
- p = argv[0];
- if (p[0] != '/') {
- option_error("ntlm_auth-helper argument must be full path");
- return 0;
- }
- p = strdup(p);
- if (p == NULL) {
- novm("ntlm_auth-helper argument");
- return 0;
- }
- if (ntlm_auth != NULL)
- free(ntlm_auth);
- ntlm_auth = p;
- return 1;
- }
- static option_t Options[] = {
- { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth,
- "Path to ntlm_auth executable", OPT_PRIV },
- { NULL }
- };
- static int
- winbind_secret_check(void);
- static int winbind_pap_auth(char *user,
- char *passwd,
- char **msgp,
- struct wordlist **paddrs,
- struct wordlist **popts);
- 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);
- static int winbind_allowed_address(u_int32_t addr);
- char pppd_version[] = VERSION;
- /**********************************************************************
- * %FUNCTION: plugin_init
- * %ARGUMENTS:
- * None
- * %RETURNS:
- * Nothing
- * %DESCRIPTION:
- * Initializes WINBIND plugin.
- ***********************************************************************/
- void
- plugin_init(void)
- {
- pap_check_hook = winbind_secret_check;
- pap_auth_hook = winbind_pap_auth;
- chap_check_hook = winbind_secret_check;
- chap_verify_hook = winbind_chap_verify;
- allowed_address_hook = winbind_allowed_address;
- /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
- chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
-
- add_options(Options);
- info("WINBIND plugin initialized.");
- }
- /**
- Routine to get hex characters and turn them into a 16 byte array.
- the array can be variable length, and any non-hex-numeric
- characters are skipped. "0xnn" or "0Xnn" is specially catered
- for.
- valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
- **/
- /*
- Unix SMB/CIFS implementation.
- Samba utility functions
-
- Copyright (C) Andrew Tridgell 1992-2001
- Copyright (C) Simo Sorce 2001-2002
- Copyright (C) Martin Pool 2003
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- size_t strhex_to_str(char *p, size_t len, const char *strhex)
- {
- size_t i;
- size_t num_chars = 0;
- unsigned char lonybble, hinybble;
- const char *hexchars = "0123456789ABCDEF";
- char *p1 = NULL, *p2 = NULL;
- for (i = 0; i < len && strhex[i] != 0; i++) {
- if (strncmp(hexchars, "0x", 2) == 0) {
- i++; /* skip two chars */
- continue;
- }
- if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
- break;
- i++; /* next hex digit */
- if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
- break;
- /* get the two nybbles */
- hinybble = (p1 - hexchars);
- lonybble = (p2 - hexchars);
- p[num_chars] = (hinybble << 4) | lonybble;
- num_chars++;
- p1 = NULL;
- p2 = NULL;
- }
- return num_chars;
- }
- static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- /**
- * Encode a base64 string into a malloc()ed string caller to free.
- *
- *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
- **/
- char * base64_encode(const char *data)
- {
- size_t out_cnt = 0;
- size_t len = strlen(data);
- size_t output_len = 4 * ((len + 2) / 3) + 2;
- const unsigned char *ptr = (const unsigned char *) data;
- char *result = malloc(output_len); /* get us plenty of space */
- unsigned int bits;
- for (; len >= 3; len -= 3) {
- bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
- ptr += 3;
- result[out_cnt++] = b64[bits >> 18];
- result[out_cnt++] = b64[(bits >> 12) & 0x3f];
- result[out_cnt++] = b64[(bits >> 6) & 0x3f];
- result[out_cnt++] = b64[bits & 0x3f];
- }
- if (len != 0) {
- bits = ptr[0] << 16;
- if (len > 1)
- bits |= ptr[1] << 8;
- result[out_cnt++] = b64[bits >> 18];
- result[out_cnt++] = b64[(bits >> 12) & 0x3f];
- result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '=';
- result[out_cnt++] = '=';
- }
- result[out_cnt] = '\0'; /* terminate */
- return result;
- }
- unsigned int 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)
- {
-
- pid_t forkret;
- int child_in[2];
- int child_out[2];
- int status;
- int authenticated = NOT_AUTHENTICATED; /* not auth */
- int got_user_session_key = 0; /* not got key */
- char buffer[1024];
- FILE *pipe_in;
- FILE *pipe_out;
-
- int i;
- char *challenge_hex;
- char *lm_hex_hash;
- char *nt_hex_hash;
- /* First see if we have a program to run... */
- if (ntlm_auth == NULL)
- return NOT_AUTHENTICATED;
- /* Make first child */
- if (pipe(child_out) == -1) {
- error("pipe creation failed for child OUT!");
- return NOT_AUTHENTICATED;
- }
- if (pipe(child_in) == -1) {
- error("pipe creation failed for child IN!");
- return NOT_AUTHENTICATED;
- }
- forkret = safe_fork(child_in[0], child_out[1], 2);
- if (forkret == -1) {
- if (error_string) {
- *error_string = strdup("fork failed!");
- }
- return NOT_AUTHENTICATED;
- }
- if (forkret == 0) {
- /* child process */
- uid_t uid;
- close(child_out[0]);
- close(child_in[1]);
- /* run winbind as the user that invoked pppd */
- setgid(getgid());
- uid = getuid();
- if (setuid(uid) == -1 || getuid() != uid)
- fatal("pppd/winbind: could not setuid to %d: %m", uid);
- execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);
- fatal("pppd/winbind: could not exec /bin/sh: %m");
- }
- /* parent */
- close(child_out[1]);
- close(child_in[0]);
- /* Need to write the User's info onto the pipe */
- pipe_in = fdopen(child_in[1], "w");
- pipe_out = fdopen(child_out[0], "r");
- /* look for session key coming back */
- if (username) {
- char *b64_username = base64_encode(username);
- fprintf(pipe_in, "Username:: %s\n", b64_username);
- free(b64_username);
- }
- if (domain) {
- char *b64_domain = base64_encode(domain);
- fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
- free(b64_domain);
- }
- if (full_username) {
- char *b64_full_username = base64_encode(full_username);
- fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
- free(b64_full_username);
- }
- if (plaintext_password) {
- char *b64_plaintext_password = base64_encode(plaintext_password);
- fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
- free(b64_plaintext_password);
- }
- if (challenge_length) {
- fprintf(pipe_in, "Request-User-Session-Key: yes\n");
- challenge_hex = malloc(challenge_length*2+1);
-
- for (i = 0; i < challenge_length; i++)
- sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
-
- fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
- free(challenge_hex);
- }
-
- if (lm_response_length) {
- lm_hex_hash = malloc(lm_response_length*2+1);
-
- for (i = 0; i < lm_response_length; i++)
- sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
-
- fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
- free(lm_hex_hash);
- }
-
- if (nt_response_length) {
- nt_hex_hash = malloc(nt_response_length*2+1);
-
- for (i = 0; i < nt_response_length; i++)
- sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
-
- fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
- free(nt_hex_hash);
- }
-
- fprintf(pipe_in, ".\n");
- fflush(pipe_in);
-
- while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
- char *message, *parameter;
- if (buffer[strlen(buffer)-1] != '\n') {
- break;
- }
- buffer[strlen(buffer)-1] = '\0';
- message = buffer;
- if (!(parameter = strstr(buffer, ": "))) {
- break;
- }
-
- parameter[0] = '\0';
- parameter++;
- parameter[0] = '\0';
- parameter++;
-
- if (strcmp(message, ".") == 0) {
- /* end of sequence */
- break;
- } else if (strcasecmp(message, "Authenticated") == 0) {
- if (strcasecmp(parameter, "Yes") == 0) {
- authenticated = AUTHENTICATED;
- } else {
- notice("Winbind has declined authentication for user!");
- authenticated = NOT_AUTHENTICATED;
- }
- } else if (strcasecmp(message, "User-session-key") == 0) {
- /* length is the number of characters to parse */
- if (nt_key) {
- if (strhex_to_str(nt_key, 32, parameter) == 16) {
- got_user_session_key = 1;
- } else {
- notice("NT session key for user was not 16 bytes!");
- }
- }
- } else if (strcasecmp(message, "Error") == 0) {
- authenticated = NOT_AUTHENTICATED;
- if (error_string)
- *error_string = strdup(parameter);
- } else if (strcasecmp(message, "Authentication-Error") == 0) {
- authenticated = NOT_AUTHENTICATED;
- if (error_string)
- *error_string = strdup(parameter);
- } else {
- notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
- }
- }
- /* parent */
- if (close(child_out[0]) == -1) {
- notice("error closing pipe?!? for child OUT[0]");
- return NOT_AUTHENTICATED;
- }
- /* parent */
- if (close(child_in[1]) == -1) {
- notice("error closing pipe?!? for child IN[1]");
- return NOT_AUTHENTICATED;
- }
- while ((wait(&status) == -1) && errno == EINTR)
- ;
- if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
- notice("Did not get user session key, despite being authenticated!");
- return NOT_AUTHENTICATED;
- }
- return authenticated;
- }
- /**********************************************************************
- * %FUNCTION: winbind_secret_check
- * %ARGUMENTS:
- * None
- * %RETURNS:
- * 0 if we don't have an ntlm_auth program to run, otherwise 1.
- * %DESCRIPTION:
- * Tells pppd that we will try to authenticate the peer, and not to
- * worry about looking in /etc/ppp/ *-secrets
- ***********************************************************************/
- static int
- winbind_secret_check(void)
- {
- return ntlm_auth != NULL;
- }
- /**********************************************************************
- * %FUNCTION: winbind_pap_auth
- * %ARGUMENTS:
- * user -- user-name of peer
- * passwd -- password supplied by peer
- * msgp -- Message which will be sent in PAP response
- * paddrs -- set to a list of possible peer IP addresses
- * popts -- set to a list of additional pppd options
- * %RETURNS:
- * 1 if we can authenticate, -1 if we cannot.
- * %DESCRIPTION:
- * Performs PAP authentication using WINBIND
- ***********************************************************************/
- static int
- winbind_pap_auth(char *user,
- char *password,
- char **msgp,
- struct wordlist **paddrs,
- struct wordlist **popts)
- {
- if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
- return 1;
- }
- return -1;
- }
- /**********************************************************************
- * %FUNCTION: winbind_chap_auth
- * %ARGUMENTS:
- * user -- user-name of peer
- * remmd -- hash received from peer
- * remmd_len -- length of remmd
- * cstate -- pppd's chap_state structure
- * %RETURNS:
- * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
- * %DESCRIPTION:
- * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
- ***********************************************************************/
- 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)
- {
- int challenge_len, response_len;
- char domainname[256];
- char *domain;
- char *username;
- char *p;
- char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
- /* The first byte of each of these strings contains their length */
- challenge_len = *challenge++;
- response_len = *response++;
-
- /* remove domain from "domain\username" */
- if ((username = strrchr(user, '\\')) != NULL)
- ++username;
- else
- username = user;
-
- strlcpy(domainname, user, sizeof(domainname));
-
- /* remove domain from "domain\username" */
- if ((p = strrchr(domainname, '\\')) != NULL) {
- *p = '\0';
- domain = domainname;
- } else {
- domain = NULL;
- }
-
- /* generate MD based on negotiated type */
- switch (digest->code) {
-
- case CHAP_MICROSOFT:
- {
- char *error_string = NULL;
- u_char *nt_response = NULL;
- u_char *lm_response = NULL;
- int nt_response_size = 0;
- int lm_response_size = 0;
- u_char session_key[16];
-
- if (response_len != MS_CHAP_RESPONSE_LEN)
- break; /* not even the right length */
-
- /* Determine which part of response to verify against */
- if (response[MS_CHAP_USENT]) {
- nt_response = &response[MS_CHAP_NTRESP];
- nt_response_size = MS_CHAP_NTRESP_LEN;
- } else {
- #ifdef MSLANMAN
- lm_response = &response[MS_CHAP_LANMANRESP];
- lm_response_size = MS_CHAP_LANMANRESP_LEN;
- #else
- /* Should really propagate this into the error packet. */
- notice("Peer request for LANMAN auth not supported");
- return NOT_AUTHENTICATED;
- #endif /* MSLANMAN */
- }
-
- /* ship off to winbind, and check */
-
- if (run_ntlm_auth(username,
- domain,
- NULL,
- NULL,
- challenge, challenge_len,
- lm_response, lm_response_size,
- nt_response, nt_response_size,
- session_key,
- &error_string) == AUTHENTICATED) {
- mppe_set_keys(challenge, session_key);
- slprintf(message, message_space, "Access granted");
- return AUTHENTICATED;
-
- } else {
- if (error_string) {
- notice(error_string);
- free(error_string);
- }
- slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
- challenge_len, challenge);
- return NOT_AUTHENTICATED;
- }
- break;
- }
-
- case CHAP_MICROSOFT_V2:
- {
- u_char Challenge[8];
- u_char session_key[MD4_SIGNATURE_SIZE];
- char *error_string = NULL;
-
- if (response_len != MS_CHAP2_RESPONSE_LEN)
- break; /* not even the right length */
-
- ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
- user, Challenge);
-
- /* ship off to winbind, and check */
-
- if (run_ntlm_auth(username,
- domain,
- NULL,
- NULL,
- Challenge, 8,
- NULL, 0,
- &response[MS_CHAP2_NTRESP],
- MS_CHAP2_NTRESP_LEN,
- session_key,
- &error_string) == AUTHENTICATED) {
-
- GenerateAuthenticatorResponse(session_key,
- &response[MS_CHAP2_NTRESP],
- &response[MS_CHAP2_PEER_CHALLENGE],
- challenge, user, saresponse);
- mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP],
- MS_CHAP2_AUTHENTICATOR);
- if (response[MS_CHAP2_FLAGS]) {
- slprintf(message, message_space, "S=%s", saresponse);
- } else {
- slprintf(message, message_space, "S=%s M=%s",
- saresponse, "Access granted");
- }
- return AUTHENTICATED;
-
- } else {
- if (error_string) {
- notice(error_string);
- slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
- challenge_len, challenge, error_string);
- free(error_string);
- } else {
- slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
- challenge_len, challenge, "Access denied");
- }
- return NOT_AUTHENTICATED;
- }
- break;
- }
-
- default:
- error("WINBIND: Challenge type %u unsupported", digest->code);
- }
- return NOT_AUTHENTICATED;
- }
- static int
- winbind_allowed_address(u_int32_t addr)
- {
- ipcp_options *wo = &ipcp_wantoptions[0];
- if (wo->hisaddr !=0 && wo->hisaddr == addr) {
- return 1;
- }
- return -1;
- }
|