Home | History | Annotate | Download | only in plugins
      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 (at) 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 (at) elgo.si>
     19 *    Copyright (C) 1996, Lars Fenneberg <in5y050 (at) public.uni-hamburg.de>
     20 *    Copyright (C) 1997, Miguel A.L. Paraz <map (at) iphil.net>
     21 *
     22 * Uses radiusclient library, which is:
     23 *    Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf (at) elemental.net>
     24 *    Copyright (C) 2002 Roaring Penguin Software Inc.
     25 *
     26 * MPPE support is by Ralf Hofmann, <ralf.hofmann (at) elvido.net>, with
     27 * modification from Frank Cusack, <frank (at) 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 
     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
    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 
    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  **/
    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 
    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
    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
    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
    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
    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