summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ppp/chap.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>1999-02-11 10:14:08 +0000
committerbrian <brian@FreeBSD.org>1999-02-11 10:14:08 +0000
commit5dc50d8ed53887553c9cb516e2b7915e55171a9f (patch)
treef9ab8c0680569eb7727b37072506b8ada5abb174 /usr.sbin/ppp/chap.c
parent4ef23fab94c614e2e42029a09de004d3a1613d62 (diff)
downloadFreeBSD-src-5dc50d8ed53887553c9cb516e2b7915e55171a9f.zip
FreeBSD-src-5dc50d8ed53887553c9cb516e2b7915e55171a9f.tar.gz
When resending chap challenges, resend the same challenge
each time rather than making up a new one. Increase the authname/authkey max sizes to 100 characters. Allow ``authkey'' specifications beginning with ``!''. When a challenge is received, the text following the ``!'' is executed as a program (expanding stuff in the same way that ``sh'' and ``!bg'' do). The program is passed the peer name, peer challenge and local ``authname'' on standard input and is expected to output the name/key combination that should be used to build the CHAP response. This provides support for Secure ID cards (guess what I was given at work recently!) using CHAP. Examples will follow.
Diffstat (limited to 'usr.sbin/ppp/chap.c')
-rw-r--r--usr.sbin/ppp/chap.c271
1 files changed, 241 insertions, 30 deletions
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
index 495fdde..1c937cd 100644
--- a/usr.sbin/ppp/chap.c
+++ b/usr.sbin/ppp/chap.c
@@ -17,7 +17,7 @@
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
- * $Id: chap.c,v 1.41 1999/02/07 13:48:38 brian Exp $
+ * $Id: chap.c,v 1.42 1999/02/07 13:56:29 brian Exp $
*
* TODO:
*/
@@ -27,13 +27,19 @@
#include <netinet/ip.h>
#include <sys/un.h>
+#include <errno.h>
+#include <fcntl.h>
#ifdef HAVE_DES
#include <md4.h>
#include <string.h>
#endif
#include <md5.h>
+#include <paths.h>
+#include <signal.h>
#include <stdlib.h>
+#include <sys/wait.h>
#include <termios.h>
+#include <unistd.h>
#include "mbuf.h"
#include "log.h"
@@ -45,10 +51,10 @@
#include "lqr.h"
#include "hdlc.h"
#include "auth.h"
-#include "chap.h"
#include "async.h"
#include "throughput.h"
#include "descriptor.h"
+#include "chap.h"
#include "iplist.h"
#include "slcompress.h"
#include "ipcp.h"
@@ -63,6 +69,7 @@
#include "bundle.h"
#include "chat.h"
#include "cbcp.h"
+#include "command.h"
#include "datalink.h"
#ifdef HAVE_DES
#include "chap_ms.h"
@@ -175,34 +182,230 @@ chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, int MSChap)
}
static void
+chap_StartChild(struct chap *chap, char *prog, const char *name)
+{
+ char *argv[MAXARGS], *nargv[MAXARGS];
+ int argc, fd;
+ int in[2], out[2];
+
+ if (chap->child.fd != -1) {
+ log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
+ return;
+ }
+
+ if (pipe(in) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ return;
+ }
+
+ if (pipe(out) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ return;
+ }
+
+ switch ((chap->child.pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ close(out[0]);
+ close(out[1]);
+ chap->child.pid = 0;
+ return;
+
+ case 0:
+ timer_TermService();
+ close(in[1]);
+ close(out[0]);
+ if (out[1] == STDIN_FILENO) {
+ fd = dup(out[1]);
+ close(out[1]);
+ out[1] = fd;
+ }
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ dup2(fd, STDERR_FILENO);
+ fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */
+
+ setuid(geteuid());
+ argc = command_Interpret(prog, strlen(prog), argv);
+ command_Expand(nargv, argc, (char const *const *)argv,
+ chap->auth.physical->dl->bundle, 0);
+ execvp(nargv[0], nargv);
+
+ log_Printf(LogWARN, "exec() of %s failed: %s\n",
+ nargv[0], strerror(errno));
+ exit(255);
+
+ default:
+ close(in[0]);
+ close(out[1]);
+ chap->child.fd = out[0];
+ chap->child.buf.len = 0;
+ write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
+ write(in[1], "\n", 1);
+ write(in[1], chap->challenge + 1, *chap->challenge);
+ write(in[1], "\n", 1);
+ write(in[1], name, strlen(name));
+ write(in[1], "\n", 1);
+ close(in[1]);
+ break;
+ }
+}
+
+static void
+chap_Cleanup(struct chap *chap, int sig)
+{
+ if (chap->child.pid) {
+ int status;
+
+ close(chap->child.fd);
+ chap->child.fd = -1;
+ if (sig)
+ kill(chap->child.pid, SIGTERM);
+ chap->child.pid = 0;
+ chap->child.buf.len = 0;
+
+ if (wait(&status) == -1)
+ log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
+ else if (WIFSIGNALED(status))
+ log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
+ else if (WIFEXITED(status) && WEXITSTATUS(status))
+ log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
+ }
+ *chap->challenge = 0;
+}
+
+static void
+chap_SendResponse(struct chap *chap, char *name, char *key)
+{
+ char *ans;
+
+ ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, 0);
+
+ if (ans) {
+ ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
+ ans, *ans + 1 + strlen(name), name);
+ free(ans);
+ } else
+ ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
+ "Out of memory!", 14, NULL);
+}
+
+static int
+chap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ if (r && chap && chap->child.fd != -1) {
+ FD_SET(chap->child.fd, r);
+ if (*n < chap->child.fd + 1)
+ *n = chap->child.fd + 1;
+ log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+chap_IsSet(struct descriptor *d, const fd_set *fdset)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
+}
+
+static void
+chap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct chap *chap = descriptor2chap(d);
+ int got;
+
+ got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
+ sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
+ if (got == -1) {
+ log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
+ chap_Cleanup(chap, SIGTERM);
+ } else if (got == 0) {
+ log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
+ chap_Cleanup(chap, SIGTERM);
+ } else {
+ char *name, *key, *end;
+
+ chap->child.buf.len += got;
+ chap->child.buf.ptr[chap->child.buf.len] = '\0';
+ name = chap->child.buf.ptr;
+ name += strspn(name, " \t");
+ if ((key = strchr(name, '\n')) == NULL)
+ end = NULL;
+ else
+ end = strchr(++key, '\n');
+
+ if (end == NULL) {
+ if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
+ log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
+ chap_Cleanup(chap, SIGTERM);
+ }
+ } else {
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ end = key - 1;
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ key += strspn(key, " \t");
+
+ chap_SendResponse(chap, name, key);
+ chap_Cleanup(chap, 0);
+ }
+ }
+}
+
+static int
+chap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+static void
chap_Challenge(struct authinfo *authp)
{
struct chap *chap = auth2chap(authp);
int len, i;
char *cp;
- randinit();
- cp = chap->challenge;
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+
+ if (!*chap->challenge) {
+ randinit();
+ cp = chap->challenge;
#ifndef NORADIUS
- if (*authp->physical->dl->bundle->radius.cfg.file) {
- /* For radius, our challenge is 16 readable NUL terminated bytes :*/
- *cp++ = 16;
- for (i = 0; i < 16; i++)
- *cp++ = (random() % 10) + '0';
- } else
+ if (*authp->physical->dl->bundle->radius.cfg.file) {
+ /* For radius, our challenge is 16 readable NUL terminated bytes :*/
+ *cp++ = 16;
+ for (i = 0; i < 16; i++)
+ *cp++ = (random() % 10) + '0';
+ } else
#endif
- {
- *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
- for (i = 0; i < *chap->challenge; i++)
- *cp++ = random() & 0xff;
+ {
+ *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
+ for (i = 0; i < *chap->challenge; i++)
+ *cp++ = random() & 0xff;
+ }
+ memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
}
-
- len = strlen(authp->physical->dl->bundle->cfg.auth.name);
- memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
- cp += len;
ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
- cp - chap->challenge, NULL);
+ 1 + *chap->challenge + len, NULL);
}
static void
@@ -232,12 +435,25 @@ chap_Failure(struct authinfo *authp)
void
chap_Init(struct chap *chap, struct physical *p)
{
+ chap->desc.type = CHAP_DESCRIPTOR;
+ chap->desc.UpdateSet = chap_UpdateSet;
+ chap->desc.IsSet = chap_IsSet;
+ chap->desc.Read = chap_Read;
+ chap->desc.Write = chap_Write;
+ chap->child.pid = 0;
+ chap->child.fd = -1;
auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
*chap->challenge = 0;
chap->using_MSChap = 0;
}
void
+chap_ReInit(struct chap *chap)
+{
+ chap_Cleanup(chap, SIGTERM);
+}
+
+void
chap_Input(struct physical *p, struct mbuf *bp)
{
struct chap *chap = &p->dl->chap;
@@ -336,17 +552,12 @@ chap_Input(struct physical *p, struct mbuf *bp)
switch (chap->auth.in.hdr.code) {
case CHAP_CHALLENGE:
- name = p->dl->bundle->cfg.auth.name;
- nlen = strlen(name);
- key = p->dl->bundle->cfg.auth.key;
- myans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, 0);
- if (myans) {
- ChapOutput(p, CHAP_RESPONSE, chap->auth.id, myans,
- *myans + 1 + nlen, name);
- free(myans);
- } else
- ChapOutput(p, CHAP_FAILURE, chap->auth.id, "Out of memory!",
- 14, NULL);
+ if (*p->dl->bundle->cfg.auth.key == '!')
+ chap_StartChild(chap, p->dl->bundle->cfg.auth.key + 1,
+ p->dl->bundle->cfg.auth.name);
+ else
+ chap_SendResponse(chap, p->dl->bundle->cfg.auth.name,
+ p->dl->bundle->cfg.auth.key);
break;
case CHAP_RESPONSE:
OpenPOWER on IntegriCloud