1/*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * upap.c - User/Password Authentication Protocol.
25 *
26 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 *
32 * 1. Redistributions of source code must retain the above copyright
33 *    notice, this list of conditions and the following disclaimer.
34 *
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in
37 *    the documentation and/or other materials provided with the
38 *    distribution.
39 *
40 * 3. The name "Carnegie Mellon University" must not be used to
41 *    endorse or promote products derived from this software without
42 *    prior written permission. For permission or any legal
43 *    details, please contact
44 *      Office of Technology Transfer
45 *      Carnegie Mellon University
46 *      5000 Forbes Avenue
47 *      Pittsburgh, PA  15213-3890
48 *      (412) 268-4387, fax: (412) 268-7395
49 *      tech-transfer@andrew.cmu.edu
50 *
51 * 4. Redistributions of any form whatsoever must retain the following
52 *    acknowledgment:
53 *    "This product includes software developed by Computing Services
54 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
55 *
56 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
57 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
58 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
59 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
60 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
61 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
62 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
63 */
64
65#define RCSID	"$Id: upap.c,v 1.8 2005/12/13 06:30:15 lindak Exp $"
66
67/*
68 * TODO:
69 */
70
71#include <stdio.h>
72#include <string.h>
73
74#include "pppd.h"
75#include "upap.h"
76
77#ifndef lint
78static const char rcsid[] = RCSID;
79#endif
80
81static bool hide_password = 1;
82
83/*
84 * Command-line options.
85 */
86static option_t pap_option_list[] = {
87    { "hide-password", o_bool, &hide_password,
88      "Don't output passwords to log", OPT_PRIO | 1 },
89    { "show-password", o_bool, &hide_password,
90      "Show password string in debug log messages", OPT_PRIOSUB | 0 },
91
92    { "pap-restart", o_int, &upap[0].us_timeouttime,
93      "Set retransmit timeout for PAP", OPT_PRIO },
94    { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
95      "Set max number of transmissions for auth-reqs", OPT_PRIO },
96    { "pap-timeout", o_int, &upap[0].us_reqtimeout,
97      "Set time limit for peer PAP authentication", OPT_PRIO },
98
99    { NULL }
100};
101
102/*
103 * Protocol entry points.
104 */
105static void upap_init __P((int));
106static void upap_lowerup __P((int));
107static void upap_lowerdown __P((int));
108static void upap_input __P((int, u_char *, int));
109static void upap_protrej __P((int));
110static int  upap_printpkt __P((u_char *, int,
111			       void (*) __P((void *, char *, ...)), void *));
112
113struct protent pap_protent = {
114    PPP_PAP,
115    upap_init,
116    upap_input,
117    upap_protrej,
118    upap_lowerup,
119    upap_lowerdown,
120    NULL,
121    NULL,
122    upap_printpkt,
123    NULL,
124    1,
125    "PAP",
126    NULL,
127    pap_option_list,
128    NULL,
129    NULL,
130    NULL,
131#ifdef __APPLE__
132    NULL,
133    NULL,
134    NULL,
135    NULL
136#endif
137};
138
139upap_state upap[NUM_PPP];		/* UPAP state; one for each unit */
140
141static void upap_timeout __P((void *));
142static void upap_reqtimeout __P((void *));
143static void upap_rauthreq __P((upap_state *, u_char *, int, int));
144static void upap_rauthack __P((upap_state *, u_char *, int, int));
145static void upap_rauthnak __P((upap_state *, u_char *, int, int));
146static void upap_sauthreq __P((upap_state *));
147static void upap_sresp __P((upap_state *, int, int, char *, int));
148
149
150/*
151 * upap_init - Initialize a UPAP unit.
152 */
153static void
154upap_init(unit)
155    int unit;
156{
157    upap_state *u = &upap[unit];
158
159    u->us_unit = unit;
160    u->us_user = NULL;
161    u->us_userlen = 0;
162    u->us_passwd = NULL;
163    u->us_passwdlen = 0;
164    u->us_clientstate = UPAPCS_INITIAL;
165    u->us_serverstate = UPAPSS_INITIAL;
166    u->us_id = 0;
167    u->us_timeouttime = UPAP_DEFTIMEOUT;
168    u->us_maxtransmits = 10;
169    u->us_reqtimeout = UPAP_DEFREQTIME;
170}
171
172
173/*
174 * upap_authwithpeer - Authenticate us with our peer (start client).
175 *
176 * Set new state and send authenticate's.
177 */
178void
179upap_authwithpeer(unit, user, password)
180    int unit;
181    char *user, *password;
182{
183    upap_state *u = &upap[unit];
184
185    /* Save the username and password we're given */
186    u->us_user = user;
187    u->us_userlen = strlen(user);
188    u->us_passwd = password;
189    u->us_passwdlen = strlen(password);
190    u->us_transmits = 0;
191
192    /* Lower layer up yet? */
193    if (u->us_clientstate == UPAPCS_INITIAL ||
194	u->us_clientstate == UPAPCS_PENDING) {
195	u->us_clientstate = UPAPCS_PENDING;
196	return;
197    }
198
199    upap_sauthreq(u);			/* Start protocol */
200}
201
202
203/*
204 * upap_authpeer - Authenticate our peer (start server).
205 *
206 * Set new state.
207 */
208void
209upap_authpeer(unit)
210    int unit;
211{
212    upap_state *u = &upap[unit];
213
214    /* Lower layer up yet? */
215    if (u->us_serverstate == UPAPSS_INITIAL ||
216	u->us_serverstate == UPAPSS_PENDING) {
217	u->us_serverstate = UPAPSS_PENDING;
218	return;
219    }
220
221    u->us_serverstate = UPAPSS_LISTEN;
222    if (u->us_reqtimeout > 0)
223	TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
224}
225
226
227/*
228 * upap_timeout - Retransmission timer for sending auth-reqs expired.
229 */
230static void
231upap_timeout(arg)
232    void *arg;
233{
234    upap_state *u = (upap_state *) arg;
235
236    if (u->us_clientstate != UPAPCS_AUTHREQ)
237	return;
238
239    if (u->us_transmits >= u->us_maxtransmits) {
240	/* give up in disgust */
241	error("No response to PAP authenticate-requests");
242	u->us_clientstate = UPAPCS_BADAUTH;
243	auth_withpeer_fail(u->us_unit, PPP_PAP);
244	return;
245    }
246
247    upap_sauthreq(u);		/* Send Authenticate-Request */
248}
249
250
251/*
252 * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
253 */
254static void
255upap_reqtimeout(arg)
256    void *arg;
257{
258    upap_state *u = (upap_state *) arg;
259
260    if (u->us_serverstate != UPAPSS_LISTEN)
261	return;			/* huh?? */
262
263    auth_peer_fail(u->us_unit, PPP_PAP);
264    u->us_serverstate = UPAPSS_BADAUTH;
265}
266
267
268/*
269 * upap_lowerup - The lower layer is up.
270 *
271 * Start authenticating if pending.
272 */
273static void
274upap_lowerup(unit)
275    int unit;
276{
277    upap_state *u = &upap[unit];
278
279    if (u->us_clientstate == UPAPCS_INITIAL)
280	u->us_clientstate = UPAPCS_CLOSED;
281    else if (u->us_clientstate == UPAPCS_PENDING) {
282	upap_sauthreq(u);	/* send an auth-request */
283    }
284
285    if (u->us_serverstate == UPAPSS_INITIAL)
286	u->us_serverstate = UPAPSS_CLOSED;
287    else if (u->us_serverstate == UPAPSS_PENDING) {
288	u->us_serverstate = UPAPSS_LISTEN;
289	if (u->us_reqtimeout > 0)
290	    TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
291    }
292}
293
294
295/*
296 * upap_lowerdown - The lower layer is down.
297 *
298 * Cancel all timeouts.
299 */
300static void
301upap_lowerdown(unit)
302    int unit;
303{
304    upap_state *u = &upap[unit];
305
306    if (u->us_clientstate == UPAPCS_AUTHREQ)	/* Timeout pending? */
307	UNTIMEOUT(upap_timeout, u);		/* Cancel timeout */
308    if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
309	UNTIMEOUT(upap_reqtimeout, u);
310
311    u->us_clientstate = UPAPCS_INITIAL;
312    u->us_serverstate = UPAPSS_INITIAL;
313}
314
315
316/*
317 * upap_protrej - Peer doesn't speak this protocol.
318 *
319 * This shouldn't happen.  In any case, pretend lower layer went down.
320 */
321static void
322upap_protrej(unit)
323    int unit;
324{
325    upap_state *u = &upap[unit];
326
327    if (u->us_clientstate == UPAPCS_AUTHREQ) {
328	error("PAP authentication failed due to protocol-reject");
329	auth_withpeer_fail(unit, PPP_PAP);
330    }
331    if (u->us_serverstate == UPAPSS_LISTEN) {
332	error("PAP authentication of peer failed (protocol-reject)");
333	auth_peer_fail(unit, PPP_PAP);
334    }
335    upap_lowerdown(unit);
336}
337
338
339/*
340 * upap_input - Input UPAP packet.
341 */
342static void
343upap_input(unit, inpacket, l)
344    int unit;
345    u_char *inpacket;
346    int l;
347{
348    upap_state *u = &upap[unit];
349    u_char *inp;
350    u_char code, id;
351    int len;
352
353    /*
354     * Parse header (code, id and length).
355     * If packet too short, drop it.
356     */
357    inp = inpacket;
358    if (l < UPAP_HEADERLEN) {
359	UPAPDEBUG(("pap_input: rcvd short header."));
360	return;
361    }
362    GETCHAR(code, inp);
363    GETCHAR(id, inp);
364    GETSHORT(len, inp);
365    if (len < UPAP_HEADERLEN) {
366	UPAPDEBUG(("pap_input: rcvd illegal length."));
367	return;
368    }
369    if (len > l) {
370	UPAPDEBUG(("pap_input: rcvd short packet."));
371	return;
372    }
373    len -= UPAP_HEADERLEN;
374
375    /*
376     * Action depends on code.
377     */
378    switch (code) {
379    case UPAP_AUTHREQ:
380	upap_rauthreq(u, inp, id, len);
381	break;
382
383    case UPAP_AUTHACK:
384	upap_rauthack(u, inp, id, len);
385	break;
386
387    case UPAP_AUTHNAK:
388	upap_rauthnak(u, inp, id, len);
389	break;
390
391    default:				/* XXX Need code reject */
392	break;
393    }
394}
395
396
397/*
398 * upap_rauth - Receive Authenticate.
399 */
400static void
401upap_rauthreq(u, inp, id, len)
402    upap_state *u;
403    u_char *inp;
404    int id;
405    int len;
406{
407    u_char ruserlen, rpasswdlen;
408    u_char *ruser, *rpasswd;
409    char rhostname[256];
410    int retcode;
411    char *msg;
412    int msglen;
413
414    if (u->us_serverstate < UPAPSS_LISTEN)
415	return;
416
417    /*
418     * If we receive a duplicate authenticate-request, we are
419     * supposed to return the same status as for the first request.
420     */
421    if (u->us_serverstate == UPAPSS_OPEN) {
422	upap_sresp(u, UPAP_AUTHACK, id, "", 0);	/* return auth-ack */
423	return;
424    }
425    if (u->us_serverstate == UPAPSS_BADAUTH) {
426	upap_sresp(u, UPAP_AUTHNAK, id, "", 0);	/* return auth-nak */
427	return;
428    }
429
430    /*
431     * Parse user/passwd.
432     */
433    if (len < 1) {
434	UPAPDEBUG(("pap_rauth: rcvd short packet."));
435	return;
436    }
437    GETCHAR(ruserlen, inp);
438    len -= sizeof (u_char) + ruserlen + sizeof (u_char);
439    if (len < 0) {
440	UPAPDEBUG(("pap_rauth: rcvd short packet."));
441	return;
442    }
443    ruser = inp;
444    INCPTR(ruserlen, inp);
445    GETCHAR(rpasswdlen, inp);
446    if (len < rpasswdlen) {
447	UPAPDEBUG(("pap_rauth: rcvd short packet."));
448	return;
449    }
450    rpasswd = inp;
451
452    /*
453     * Check the username and password given.
454     */
455    retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
456			   rpasswdlen, &msg);
457    BZERO(rpasswd, rpasswdlen);
458
459    /*
460     * Check remote number authorization.  A plugin may have filled in
461     * the remote number or added an allowed number, and rather than
462     * return an authenticate failure, is leaving it for us to verify.
463     */
464    if (retcode == UPAP_AUTHACK) {
465	if (!auth_number()) {
466	    /* We do not want to leak info about the pap result. */
467	    retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
468	    warning("calling number %q is not authorized", remote_number);
469	}
470    }
471
472    msglen = strlen(msg);
473    if (msglen > 255)
474	msglen = 255;
475    upap_sresp(u, retcode, id, msg, msglen);
476
477    /* Null terminate and clean remote name. */
478    slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
479
480    if (retcode == UPAP_AUTHACK) {
481	u->us_serverstate = UPAPSS_OPEN;
482	notice("PAP peer authentication succeeded for %q", rhostname);
483	auth_peer_success(u->us_unit, PPP_PAP, 0, ruser, ruserlen);
484    } else {
485	u->us_serverstate = UPAPSS_BADAUTH;
486	warning("PAP peer authentication failed for %q", rhostname);
487	auth_peer_fail(u->us_unit, PPP_PAP);
488    }
489
490    if (u->us_reqtimeout > 0)
491	UNTIMEOUT(upap_reqtimeout, u);
492}
493
494
495/*
496 * upap_rauthack - Receive Authenticate-Ack.
497 */
498static void
499upap_rauthack(u, inp, id, len)
500    upap_state *u;
501    u_char *inp;
502    int id;
503    int len;
504{
505    u_char msglen;
506    char *msg;
507
508    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
509	return;
510
511    /*
512     * Parse message.
513     */
514    if (len < 1) {
515	UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
516    } else {
517	GETCHAR(msglen, inp);
518	if (msglen > 0) {
519	    len -= sizeof (u_char);
520	    if (len < msglen) {
521		UPAPDEBUG(("pap_rauthack: rcvd short packet."));
522		return;
523	    }
524	    msg = (char *) inp;
525	    PRINTMSG(msg, msglen);
526	}
527    }
528
529    u->us_clientstate = UPAPCS_OPEN;
530
531    notice("PAP authentication succeeded");
532    auth_withpeer_success(u->us_unit, PPP_PAP, 0);
533}
534
535
536/*
537 * upap_rauthnak - Receive Authenticate-Nak.
538 */
539static void
540upap_rauthnak(u, inp, id, len)
541    upap_state *u;
542    u_char *inp;
543    int id;
544    int len;
545{
546    u_char msglen;
547    char *msg;
548
549    if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
550	return;
551
552    /*
553     * Parse message.
554     */
555    if (len < 1) {
556	UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
557    } else {
558	GETCHAR(msglen, inp);
559	if (msglen > 0) {
560	    len -= sizeof (u_char);
561	    if (len < msglen) {
562		UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
563		return;
564	    }
565	    msg = (char *) inp;
566	    PRINTMSG(msg, msglen);
567	}
568    }
569
570    u->us_clientstate = UPAPCS_BADAUTH;
571
572    error("PAP authentication failed");
573    auth_withpeer_fail(u->us_unit, PPP_PAP);
574}
575
576
577/*
578 * upap_sauthreq - Send an Authenticate-Request.
579 */
580static void
581upap_sauthreq(u)
582    upap_state *u;
583{
584    u_char *outp;
585    int outlen;
586
587    outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
588	u->us_userlen + u->us_passwdlen;
589    outp = outpacket_buf;
590
591    MAKEHEADER(outp, PPP_PAP);
592
593    PUTCHAR(UPAP_AUTHREQ, outp);
594    PUTCHAR(++u->us_id, outp);
595    PUTSHORT(outlen, outp);
596    PUTCHAR(u->us_userlen, outp);
597    BCOPY(u->us_user, outp, u->us_userlen);
598    INCPTR(u->us_userlen, outp);
599    PUTCHAR(u->us_passwdlen, outp);
600    BCOPY(u->us_passwd, outp, u->us_passwdlen);
601
602    output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
603
604    TIMEOUT(upap_timeout, u, u->us_timeouttime);
605    ++u->us_transmits;
606    u->us_clientstate = UPAPCS_AUTHREQ;
607}
608
609
610/*
611 * upap_sresp - Send a response (ack or nak).
612 */
613static void
614upap_sresp(u, code, id, msg, msglen)
615    upap_state *u;
616    u_char code, id;
617    char *msg;
618    int msglen;
619{
620    u_char *outp;
621    int outlen;
622
623    outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
624    outp = outpacket_buf;
625    MAKEHEADER(outp, PPP_PAP);
626
627    PUTCHAR(code, outp);
628    PUTCHAR(id, outp);
629    PUTSHORT(outlen, outp);
630    PUTCHAR(msglen, outp);
631    BCOPY(msg, outp, msglen);
632    output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
633}
634
635/*
636 * upap_printpkt - print the contents of a PAP packet.
637 */
638static char *upap_codenames[] = {
639    "AuthReq", "AuthAck", "AuthNak"
640};
641
642static int
643upap_printpkt(p, plen, printer, arg)
644    u_char *p;
645    int plen;
646    void (*printer) __P((void *, char *, ...));
647    void *arg;
648{
649    int code, id, len;
650    int mlen, ulen, wlen;
651    char *user, *pwd, *msg;
652    u_char *pstart;
653
654    if (plen < UPAP_HEADERLEN)
655	return 0;
656    pstart = p;
657    GETCHAR(code, p);
658    GETCHAR(id, p);
659    GETSHORT(len, p);
660    if (len < UPAP_HEADERLEN || len > plen)
661	return 0;
662
663    if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
664	printer(arg, " %s", upap_codenames[code-1]);
665    else
666	printer(arg, " code=0x%x", code);
667    printer(arg, " id=0x%x", id);
668    len -= UPAP_HEADERLEN;
669    switch (code) {
670    case UPAP_AUTHREQ:
671	if (len < 1)
672	    break;
673	ulen = p[0];
674	if (len < ulen + 2)
675	    break;
676	wlen = p[ulen + 1];
677	if (len < ulen + wlen + 2)
678	    break;
679	user = (char *) (p + 1);
680	pwd = (char *) (p + ulen + 2);
681	p += ulen + wlen + 2;
682	len -= ulen + wlen + 2;
683	printer(arg, " user=");
684	print_string(user, ulen, printer, arg);
685	printer(arg, " password=");
686	if (!hide_password)
687	    print_string(pwd, wlen, printer, arg);
688	else
689	    printer(arg, "<hidden>");
690	break;
691    case UPAP_AUTHACK:
692    case UPAP_AUTHNAK:
693	if (len < 1)
694	    break;
695	mlen = p[0];
696	if (len < mlen + 1)
697	    break;
698	msg = (char *) (p + 1);
699	p += mlen + 1;
700	len -= mlen + 1;
701	printer(arg, " ");
702	print_string(msg, mlen, printer, arg);
703	break;
704    }
705
706    /* print the rest of the bytes in the packet */
707    for (; len > 0; --len) {
708	GETCHAR(code, p);
709	printer(arg, " %.2x", code);
710    }
711
712    return p - pstart;
713}
714