* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The name(s) of the authors of this software must not be used to
+ * 2. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
- * 4. Redistributions of any form whatsoever must retain the following
+ * 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define RCSID "$Id: chap-new.c,v 1.5 2004/10/31 22:23:18 paulus Exp $"
+#define RCSID "$Id: chap-new.c,v 1.9 2007/06/19 02:08:35 carlsonj Exp $"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
#include <stdlib.h>
#include <string.h>
#include "pppd.h"
+#include "session.h"
#include "chap-new.h"
#include "chap-md5.h"
-#ifdef CHAPMS
+#ifdef PPP_WITH_CHAPMS
#include "chap_ms.h"
#define MDTYPE_ALL (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
#else
/*
* Option variables.
*/
-int chap_timeout_time = 3;
+int chap_server_timeout_time = 3;
int chap_max_transmits = 10;
int chap_rechallenge_time = 0;
+int chap_client_timeout_time = 60;
+int chapms_strip_domain = 0;
/*
* Command-line options.
*/
static option_t chap_option_list[] = {
- { "chap-restart", o_int, &chap_timeout_time,
- "Set timeout for CHAP", OPT_PRIO },
+ { "chap-restart", o_int, &chap_server_timeout_time,
+ "Set timeout for CHAP (as server)", OPT_PRIO },
{ "chap-max-challenge", o_int, &chap_max_transmits,
"Set max #xmits for challenge", OPT_PRIO },
{ "chap-interval", o_int, &chap_rechallenge_time,
"Set interval for rechallenge", OPT_PRIO },
+ { "chap-timeout", o_int, &chap_client_timeout_time,
+ "Set timeout for CHAP (as client)", OPT_PRIO },
+ { "chapms-strip-domain", o_bool, &chapms_strip_domain,
+ "Strip the domain prefix before the Username", 1 },
{ NULL }
};
int challenge_xmits;
int challenge_pktlen;
unsigned char challenge[CHAL_MAX_PKTLEN];
+ char message[256];
} server;
/* Values for flags in chap_client_state and chap_server_state */
static void chap_init(int unit);
static void chap_lowerup(int unit);
static void chap_lowerdown(int unit);
-static void chap_timeout(void *arg);
+static void chap_server_timeout(void *arg);
+static void chap_client_timeout(void *arg);
static void chap_generate_challenge(struct chap_server_state *ss);
static void chap_handle_response(struct chap_server_state *ss, int code,
unsigned char *pkt, int len);
static void chap_protrej(int unit);
static void chap_input(int unit, unsigned char *pkt, int pktlen);
static int chap_print_pkt(unsigned char *p, int plen,
- void (*printer) __P((void *, char *, ...)), void *arg);
+ void (*printer)(void *, char *, ...), void *arg);
/* List of digest types that we know about */
static struct chap_digest_type *chap_digests;
memset(&server, 0, sizeof(server));
chap_md5_init();
-#ifdef CHAPMS
+#ifdef PPP_WITH_CHAPMS
chapms_init();
#endif
}
chap_digests = dp;
}
+/*
+ * Lookup a digest type by code
+ */
+struct chap_digest_type *
+chap_find_digest(int digest_code) {
+ struct chap_digest_type *dp = NULL;
+ for (dp = chap_digests; dp != NULL; dp = dp->next)
+ if (dp->code == digest_code)
+ break;
+ return dp;
+}
+
/*
* chap_lowerup - we can start doing stuff now.
*/
cs->flags |= LOWERUP;
ss->flags |= LOWERUP;
if (ss->flags & AUTH_STARTED)
- chap_timeout(ss);
+ chap_server_timeout(ss);
}
static void
struct chap_client_state *cs = &client;
struct chap_server_state *ss = &server;
+ if (cs->flags & TIMEOUT_PENDING)
+ UNTIMEOUT(chap_client_timeout, cs);
cs->flags = 0;
if (ss->flags & TIMEOUT_PENDING)
- UNTIMEOUT(chap_timeout, ss);
+ UNTIMEOUT(chap_server_timeout, ss);
ss->flags = 0;
}
error("CHAP: peer authentication already started!");
return;
}
- for (dp = chap_digests; dp != NULL; dp = dp->next)
- if (dp->code == digest_code)
- break;
+
+ dp = chap_find_digest(digest_code);
if (dp == NULL)
fatal("CHAP digest 0x%x requested but not available",
digest_code);
ss->id = (unsigned char)(drand48() * 256);
ss->flags |= AUTH_STARTED;
if (ss->flags & LOWERUP)
- chap_timeout(ss);
+ chap_server_timeout(ss);
}
/*
cs->digest = dp;
cs->name = our_name;
- cs->flags |= AUTH_STARTED;
+ cs->flags |= AUTH_STARTED | TIMEOUT_PENDING;
+ TIMEOUT(chap_client_timeout, cs, chap_client_timeout_time);
}
/*
- * chap_timeout - It's time to send another challenge to the peer.
+ * chap_server_timeout - It's time to send another challenge to the peer.
* This could be either a retransmission of a previous challenge,
* or a new challenge to start re-authentication.
*/
static void
-chap_timeout(void *arg)
+chap_server_timeout(void *arg)
{
struct chap_server_state *ss = arg;
output(0, ss->challenge, ss->challenge_pktlen);
++ss->challenge_xmits;
ss->flags |= TIMEOUT_PENDING;
- TIMEOUT(chap_timeout, arg, chap_timeout_time);
+ TIMEOUT(chap_server_timeout, arg, chap_server_timeout_time);
+}
+
+/* chap_client_timeout - Authentication with peer timed out. */
+static void
+chap_client_timeout(void *arg)
+{
+ struct chap_client_state *cs = arg;
+
+ cs->flags &= ~TIMEOUT_PENDING;
+ cs->flags |= AUTH_DONE | AUTH_FAILED;
+ error("CHAP authentication timed out");
+ auth_withpeer_fail(0, PPP_CHAP);
}
/*
int (*verifier)(char *, char *, int, struct chap_digest_type *,
unsigned char *, unsigned char *, char *, int);
char rname[MAXNAMELEN+1];
- char message[256];
if ((ss->flags & LOWERUP) == 0)
return;
if (id != ss->challenge[PPP_HDRLEN+1] || len < 2)
return;
- if ((ss->flags & AUTH_DONE) == 0) {
- if ((ss->flags & CHALLENGE_VALID) == 0)
- return;
+ if (ss->flags & CHALLENGE_VALID) {
response = pkt;
GETCHAR(response_len, pkt);
len -= response_len + 1; /* length of name */
if (len < 0)
return;
- ss->flags &= ~CHALLENGE_VALID;
if (ss->flags & TIMEOUT_PENDING) {
ss->flags &= ~TIMEOUT_PENDING;
- UNTIMEOUT(chap_timeout, ss);
+ UNTIMEOUT(chap_server_timeout, ss);
}
if (explicit_remote) {
/* Null terminate and clean remote name. */
slprintf(rname, sizeof(rname), "%.*v", len, name);
name = rname;
+
+ /* strip the MS domain name */
+ if (chapms_strip_domain && strrchr(rname, '\\')) {
+ char tmp[MAXNAMELEN+1];
+
+ strcpy(tmp, strrchr(rname, '\\') + 1);
+ strcpy(rname, tmp);
+ }
}
if (chap_verify_hook)
verifier = chap_verify_response;
ok = (*verifier)(name, ss->name, id, ss->digest,
ss->challenge + PPP_HDRLEN + CHAP_HDRLEN,
- response, message, sizeof(message));
+ response, ss->message, sizeof(ss->message));
if (!ok || !auth_number()) {
ss->flags |= AUTH_FAILED;
warn("Peer %q failed CHAP authentication", name);
}
- }
+ } else if ((ss->flags & AUTH_DONE) == 0)
+ return;
/* send the response */
p = outpacket_buf;
MAKEHEADER(p, PPP_CHAP);
- mlen = strlen(message);
+ mlen = strlen(ss->message);
len = CHAP_HDRLEN + mlen;
p[0] = (ss->flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
p[1] = id;
p[2] = len >> 8;
p[3] = len;
if (mlen > 0)
- memcpy(p + CHAP_HDRLEN, message, mlen);
+ memcpy(p + CHAP_HDRLEN, ss->message, mlen);
output(0, outpacket_buf, PPP_HDRLEN + len);
- if ((ss->flags & AUTH_DONE) == 0) {
- ss->flags |= AUTH_DONE;
+ if (ss->flags & CHALLENGE_VALID) {
+ ss->flags &= ~CHALLENGE_VALID;
+ if (!(ss->flags & AUTH_DONE) && !(ss->flags & AUTH_FAILED)) {
+ /*
+ * Auth is OK, so now we need to check session restrictions
+ * to ensure everything is OK, but only if we used a
+ * plugin, and only if we're configured to check. This
+ * allows us to do PAM checks on PPP servers that
+ * authenticate against ActiveDirectory, and use AD for
+ * account info (like when using Winbind integrated with
+ * PAM).
+ */
+ if (session_mgmt &&
+ session_check(name, NULL, devnam, NULL) == 0) {
+ ss->flags |= AUTH_FAILED;
+ warn("Peer %q failed CHAP Session verification", name);
+ }
+ }
if (ss->flags & AUTH_FAILED) {
auth_peer_fail(0, PPP_CHAP);
} else {
- auth_peer_success(0, PPP_CHAP, ss->digest->code,
- name, strlen(name));
+ if ((ss->flags & AUTH_DONE) == 0)
+ auth_peer_success(0, PPP_CHAP,
+ ss->digest->code,
+ name, strlen(name));
if (chap_rechallenge_time) {
ss->flags |= TIMEOUT_PENDING;
- TIMEOUT(chap_timeout, ss,
+ TIMEOUT(chap_server_timeout, ss,
chap_rechallenge_time);
}
}
+ ss->flags |= AUTH_DONE;
}
}
return;
cs->flags |= AUTH_DONE;
+ UNTIMEOUT(chap_client_timeout, cs);
+ cs->flags &= ~TIMEOUT_PENDING;
+
if (code == CHAP_SUCCESS) {
/* used for MS-CHAP v2 mutual auth, yuck */
if (cs->digest->check_success != NULL) {
- if (!(*cs->digest->check_success)(pkt, len, cs->priv))
+ if (!(*cs->digest->check_success)(id, pkt, len))
code = CHAP_FAILURE;
} else
msg = "CHAP authentication succeeded";
auth_withpeer_success(0, PPP_CHAP, cs->digest->code);
else {
cs->flags |= AUTH_FAILED;
+ error("CHAP authentication failed");
auth_withpeer_fail(0, PPP_CHAP);
}
}
if (ss->flags & TIMEOUT_PENDING) {
ss->flags &= ~TIMEOUT_PENDING;
- UNTIMEOUT(chap_timeout, ss);
+ UNTIMEOUT(chap_server_timeout, ss);
}
if (ss->flags & AUTH_STARTED) {
ss->flags = 0;
}
if ((cs->flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
cs->flags &= ~AUTH_STARTED;
+ error("CHAP authentication failed due to protocol-reject");
auth_withpeer_fail(0, PPP_CHAP);
}
}
static int
chap_print_pkt(unsigned char *p, int plen,
- void (*printer) __P((void *, char *, ...)), void *arg)
+ void (*printer)(void *, char *, ...), void *arg)
{
int code, id, len;
int clen, nlen;