/* * auth.c * * Written by Archie Cobbs * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved. * See ``COPYRIGHT.whistle'' */ #include "ppp.h" #include "auth.h" #include "pap.h" #include "chap.h" #include "lcp.h" #include "log.h" #include "ngfunc.h" #include "msoft.h" #include "util.h" #ifdef USE_PAM #include #endif #ifdef USE_SYSTEM #if __FreeBSD_version >= 900007 #include #else #include #include #endif #endif /* * DEFINITIONS */ #ifdef USE_OPIE #define OPIE_ALG_MD5 5 #endif /* * INTERNAL FUNCTIONS */ static void AuthTimeout(void *arg); static int AuthGetExternalPassword(const char *extcmd, char *authname, char *password, size_t passlen); static void AuthAsync(void *arg); static void AuthAsyncFinish(void *arg, int was_canceled); static int AuthPreChecks(AuthData auth); static void AuthAccount(void *arg); static void AuthAccountFinish(void *arg, int was_canceled); static void AuthInternal(AuthData auth); static int AuthExternal(AuthData auth); static int AuthExternalAcct(AuthData auth); #ifdef USE_SYSTEM static void AuthSystem(AuthData auth); static int AuthSystemAcct(AuthData auth); #endif #ifdef USE_PAM static void AuthPAM(AuthData auth); static int AuthPAMAcct(AuthData auth); static int pam_conv(int n, const struct pam_message **msg, struct pam_response **resp, void *data); #endif #ifdef USE_OPIE static void AuthOpie(AuthData auth); #endif static const char *AuthCode(int proto, u_char code, char *buf, size_t len); static int AuthSetCommand(Context ctx, int ac, const char *const av[], const void *arg); /* Set menu options */ enum { SET_ACCEPT, SET_DENY, SET_ENABLE, SET_DISABLE, SET_YES, SET_NO, SET_AUTHNAME, SET_PASSWORD, SET_EXTAUTH_SCRIPT, SET_EXTACCT_SCRIPT, SET_MAX_LOGINS, SET_ACCT_UPDATE, SET_ACCT_UPDATE_LIMIT_IN, SET_ACCT_UPDATE_LIMIT_OUT, SET_TIMEOUT }; /* * GLOBAL VARIABLES */ const struct cmdtab AuthSetCmds[] = { {"max-logins {num} [CI]", "Max concurrent logins", AuthSetCommand, NULL, 2, (void *)SET_MAX_LOGINS}, {"authname {name}", "Authentication name", AuthSetCommand, NULL, 2, (void *)SET_AUTHNAME}, {"password {pass}", "Authentication password", AuthSetCommand, NULL, 2, (void *)SET_PASSWORD}, {"extauth-script {script}", "Authentication script", AuthSetCommand, NULL, 2, (void *)SET_EXTAUTH_SCRIPT}, {"extacct-script {script}", "Accounting script", AuthSetCommand, NULL, 2, (void *)SET_EXTACCT_SCRIPT}, {"acct-update {seconds}", "set update interval", AuthSetCommand, NULL, 2, (void *)SET_ACCT_UPDATE}, {"update-limit-in {bytes}", "set update suppresion limit", AuthSetCommand, NULL, 2, (void *)SET_ACCT_UPDATE_LIMIT_IN}, {"update-limit-out {bytes}", "set update suppresion limit", AuthSetCommand, NULL, 2, (void *)SET_ACCT_UPDATE_LIMIT_OUT}, {"timeout {seconds}", "set auth timeout", AuthSetCommand, NULL, 2, (void *)SET_TIMEOUT}, {"accept [opt ...]", "Accept option", AuthSetCommand, NULL, 2, (void *)SET_ACCEPT}, {"deny [opt ...]", "Deny option", AuthSetCommand, NULL, 2, (void *)SET_DENY}, {"enable [opt ...]", "Enable option", AuthSetCommand, NULL, 2, (void *)SET_ENABLE}, {"disable [opt ...]", "Disable option", AuthSetCommand, NULL, 2, (void *)SET_DISABLE}, {"yes [opt ...]", "Enable and accept option", AuthSetCommand, NULL, 2, (void *)SET_YES}, {"no [opt ...]", "Disable and deny option", AuthSetCommand, NULL, 2, (void *)SET_NO}, {NULL, NULL, NULL, NULL, 0, NULL}, }; const u_char gMsoftZeros[32]; static unsigned gMaxLogins = 0; /* max number of concurrent logins per * user */ static unsigned gMaxLoginsCI = 0; /* * INTERNAL VARIABLES */ static const struct confinfo gConfList[] = { {0, AUTH_CONF_RADIUS_AUTH, "radius-auth"}, {0, AUTH_CONF_RADIUS_ACCT, "radius-acct"}, {0, AUTH_CONF_INTERNAL, "internal"}, {0, AUTH_CONF_EXT_AUTH, "ext-auth"}, {0, AUTH_CONF_EXT_ACCT, "ext-acct"}, #ifdef USE_SYSTEM {0, AUTH_CONF_SYSTEM_AUTH, "system-auth"}, {0, AUTH_CONF_SYSTEM_ACCT, "system-acct"}, #endif #ifdef USE_PAM {0, AUTH_CONF_PAM_AUTH, "pam-auth"}, {0, AUTH_CONF_PAM_ACCT, "pam-acct"}, #endif #ifdef USE_OPIE {0, AUTH_CONF_OPIE, "opie"}, #endif {0, AUTH_CONF_ACCT_MANDATORY, "acct-mandatory"}, {0, 0, NULL}, }; void ACLCopy(struct acl *src, struct acl **dst) { while (src != NULL) { *dst = Mdup(MB_AUTH, src, sizeof(struct acl) + strlen(src->rule)); src = src->next; dst = &((*dst)->next); }; *dst = NULL; } void ACLDestroy(struct acl *acl) { struct acl *acl1; while (acl != NULL) { acl1 = acl->next; Freee(acl); acl = acl1; }; } void authparamsInit(struct authparams *ap) { memset(ap, 0, sizeof(struct authparams)); #ifdef USE_RADIUS ap->eapmsg = NULL; ap->state = NULL; ap->class = NULL; ap->filter_id = NULL; #endif ap->msdomain = NULL; #ifdef SIOCSIFDESCR ap->ifdescr = NULL; #endif SLIST_INIT(&ap->routes); } void authparamsDestroy(struct authparams *ap) { IfaceRoute r; #ifdef USE_NG_BPF int i; #endif #ifdef USE_RADIUS Freee(ap->eapmsg); Freee(ap->state); Freee(ap->class); Freee(ap->filter_id); #endif #ifdef USE_IPFW ACLDestroy(ap->acl_rule); ACLDestroy(ap->acl_pipe); ACLDestroy(ap->acl_queue); ACLDestroy(ap->acl_table); #endif /* USE_IPFW */ #ifdef USE_NG_BPF for (i = 0; i < ACL_FILTERS; i++) ACLDestroy(ap->acl_filters[i]); for (i = 0; i < ACL_DIRS; i++) ACLDestroy(ap->acl_limits[i]); #endif /* USE_NG_BPF */ while ((r = SLIST_FIRST(&ap->routes)) != NULL) { SLIST_REMOVE_HEAD(&ap->routes, next); Freee(r); } Freee(ap->msdomain); #ifdef SIOCSIFDESCR Freee(ap->ifdescr); #endif memset(ap, 0, sizeof(struct authparams)); } void authparamsCopy(struct authparams *src, struct authparams *dst) { IfaceRoute r, r1; #ifdef USE_NG_BPF int i; #endif memcpy(dst, src, sizeof(struct authparams)); #ifdef USE_RADIUS if (src->eapmsg) dst->eapmsg = Mdup(MB_AUTH, src->eapmsg, src->eapmsg_len); if (src->state) dst->state = Mdup(MB_AUTH, src->state, src->state_len); if (src->class) dst->class = Mdup(MB_AUTH, src->class, src->class_len); if (src->filter_id) dst->filter_id = Mstrdup(MB_AUTH, src->filter_id); #endif #ifdef USE_IPFW ACLCopy(src->acl_rule, &dst->acl_rule); ACLCopy(src->acl_pipe, &dst->acl_pipe); ACLCopy(src->acl_queue, &dst->acl_queue); ACLCopy(src->acl_table, &dst->acl_table); #endif /* USE_IPFW */ #ifdef USE_NG_BPF for (i = 0; i < ACL_FILTERS; i++) ACLCopy(src->acl_filters[i], &dst->acl_filters[i]); for (i = 0; i < ACL_DIRS; i++) ACLCopy(src->acl_limits[i], &dst->acl_limits[i]); #endif SLIST_INIT(&dst->routes); SLIST_FOREACH(r, &src->routes, next) { r1 = Mdup(MB_AUTH, r, sizeof(*r1)); SLIST_INSERT_HEAD(&dst->routes, r1, next); } if (src->msdomain) dst->msdomain = Mstrdup(MB_AUTH, src->msdomain); #ifdef SIOCSIFDESCR if (src->ifdescr) dst->ifdescr = Mstrdup(MB_AUTH, src->ifdescr); #endif } void authparamsMove(struct authparams *src, struct authparams *dst) { memcpy(dst, src, sizeof(struct authparams)); memset(src, 0, sizeof(struct authparams)); } /* * AuthInit() */ void AuthInit(Link l) { AuthConf const ac = &l->lcp.auth.conf; ac->timeout = 40; Enable(&ac->options, AUTH_CONF_INTERNAL); Enable(&ac->options, AUTH_CONF_ACCT_MANDATORY); #ifdef USE_RADIUS EapInit(l); RadiusInit(l); #endif } /* * AuthInst() * * Instantiate auth structure from template */ void AuthInst(Auth auth, Auth autht) { memcpy(auth, autht, sizeof(*auth)); if (auth->conf.extauth_script) autht->conf.extauth_script = Mstrdup(MB_AUTH, auth->conf.extauth_script); if (auth->conf.extacct_script) autht->conf.extacct_script = Mstrdup(MB_AUTH, auth->conf.extacct_script); } /* * AuthShutdown() */ void AuthShutdown(Link l) { Auth a = &l->lcp.auth; if (a->thread) paction_cancel(&a->thread); if (a->acct_thread) paction_cancel(&a->acct_thread); Freee(a->conf.extauth_script); Freee(a->conf.extacct_script); } /* * AuthStart() * * Initialize authorization info for a link */ void AuthStart(Link l) { Auth a = &l->lcp.auth; /* generate a uniq session id */ snprintf(l->session_id, AUTH_MAX_SESSIONID, "%d-%s", (int)(time(NULL) % 10000000), l->name); authparamsInit(&a->params); /* What auth protocols were negotiated by LCP? */ a->self_to_peer = l->lcp.peer_auth; a->peer_to_self = l->lcp.want_auth; a->self_to_peer_alg = l->lcp.peer_alg; a->peer_to_self_alg = l->lcp.want_alg; /* remember self's name */ PhysGetSelfName(l, a->params.selfname, sizeof(a->params.selfname)); /* remember peer's name */ PhysGetPeerName(l, a->params.peername, sizeof(a->params.peername)); /* remember self's IP address */ PhysGetSelfAddr(l, a->params.selfaddr, sizeof(a->params.selfaddr)); /* remember peer's IP address */ PhysGetPeerAddr(l, a->params.peeraddr, sizeof(a->params.peeraddr)); /* remember peer's TCP or UDP port */ PhysGetPeerPort(l, a->params.peerport, sizeof(a->params.peerport)); /* remember peer's MAC address */ PhysGetPeerMacAddr(l, a->params.peermacaddr, sizeof(a->params.peermacaddr)); /* remember peer's iface */ PhysGetPeerIface(l, a->params.peeriface, sizeof(a->params.peeriface)); /* remember calling number */ PhysGetCallingNum(l, a->params.callingnum, sizeof(a->params.callingnum)); /* remember called number */ PhysGetCalledNum(l, a->params.callednum, sizeof(a->params.callednum)); Log(LG_AUTH, ("[%s] %s: auth: peer wants %s, I want %s", Pref(&l->lcp.fsm), Fsm(&l->lcp.fsm), a->self_to_peer ? ProtoName(a->self_to_peer) : "nothing", a->peer_to_self ? ProtoName(a->peer_to_self) : "nothing")); /* Is there anything to do? */ if (!a->self_to_peer && !a->peer_to_self) { LcpAuthResult(l, TRUE); return; } /* Start global auth timer */ TimerInit(&a->timer, "AuthTimer", l->lcp.auth.conf.timeout * SECONDS, AuthTimeout, l); TimerStart(&a->timer); /* Start my auth to him */ switch (a->self_to_peer) { case 0: break; case PROTO_PAP: PapStart(l, AUTH_SELF_TO_PEER); break; case PROTO_CHAP: ChapStart(l, AUTH_SELF_TO_PEER); break; #ifdef USE_RADIUS case PROTO_EAP: EapStart(l, AUTH_SELF_TO_PEER); break; #endif default: assert(0); } /* Start his auth to me */ switch (a->peer_to_self) { case 0: break; case PROTO_PAP: PapStart(l, AUTH_PEER_TO_SELF); break; case PROTO_CHAP: ChapStart(l, AUTH_PEER_TO_SELF); break; #ifdef USE_RADIUS case PROTO_EAP: EapStart(l, AUTH_PEER_TO_SELF); break; #endif default: assert(0); } } /* * AuthInput() * * Deal with PAP/CHAP/EAP packet * But cannot be called for EAP packet if RADIUS support is not compiled in. */ void AuthInput(Link l, int proto, Mbuf bp) { AuthData auth; struct fsmheader fsmh; u_char *pkt; char buf[16]; u_short len; uint16_t fsmh_len; /* Sanity check */ if (l->lcp.phase != PHASE_AUTHENTICATE && l->lcp.phase != PHASE_NETWORK) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: rec'd stray packet", l->name)); mbfree(bp); return; } len = MBLEN(bp); /* Sanity check length */ if (len < sizeof(fsmh)) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: rec'd runt packet: %hu bytes", l->name, len)); mbfree(bp); return; } bp = mbread(bp, &fsmh, sizeof(fsmh)); fsmh_len = ntohs(fsmh.length); if (len > fsmh_len) { /* Sanity check length */ if (fsmh_len < sizeof(fsmh)) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: bad length: says %hu, rec'd %hu", l->name, fsmh_len, len)); mbfree(bp); return; } len = fsmh_len; } len -= sizeof(fsmh); pkt = MBDATA(bp); #ifdef USE_RADIUS if (proto == PROTO_EAP && bp) { Log(LG_AUTH, ("[%s] %s: rec'd %s #%d len: %hu, type: %s", l->name, ProtoName(proto), AuthCode(proto, fsmh.code, buf, sizeof(buf)), fsmh.id, fsmh_len, EapType(pkt[0]))); } else #endif Log(LG_AUTH, ("[%s] %s: rec'd %s #%d len: %hu", l->name, ProtoName(proto), AuthCode(proto, fsmh.code, buf, sizeof(buf)), fsmh.id, fsmh_len)); auth = AuthDataNew(l); auth->proto = proto; auth->id = fsmh.id; auth->code = fsmh.code; /* Status defaults to undefined */ auth->status = AUTH_STATUS_UNDEF; switch (proto) { case PROTO_PAP: PapInput(l, auth, pkt, len); break; case PROTO_CHAP: ChapInput(l, auth, pkt, len); break; #ifdef USE_RADIUS case PROTO_EAP: EapInput(l, auth, pkt, len); break; #endif default: assert(0); } mbfree(bp); } /* * AuthOutput() * */ void AuthOutput(Link l, int proto, u_int code, u_int id, const u_char *ptr, int len, int add_len, u_char eap_type) { struct fsmheader lh; Mbuf bp; int plen; char buf[32]; add_len = !!add_len; /* Setup header */ if (proto == PROTO_EAP) plen = sizeof(lh) + len + add_len + 1; else plen = sizeof(lh) + len + add_len; lh.code = code; lh.id = id; lh.length = htons(plen); /* Build packet */ bp = mbcopyback(NULL, 0, &lh, sizeof(lh)); if (proto == PROTO_EAP) bp = mbcopyback(bp, MBLEN(bp), &eap_type, 1); if (add_len) { u_char tl = len; bp = mbcopyback(bp, MBLEN(bp), &tl, 1); } bp = mbcopyback(bp, MBLEN(bp), ptr, len); #ifdef USE_RADIUS if (proto == PROTO_EAP) { Log(LG_AUTH, ("[%s] %s: sending %s #%d len: %d, type: %s", l->name, ProtoName(proto), AuthCode(proto, code, buf, sizeof(buf)), id, plen, EapType(eap_type))); } else #endif Log(LG_AUTH, ("[%s] %s: sending %s #%d len: %d", l->name, ProtoName(proto), AuthCode(proto, code, buf, sizeof(buf)), id, plen)); /* Send it out */ NgFuncWritePppFrameLink(l, proto, bp); } /* * AuthFinish() * * Authorization is finished, so continue one way or the other */ void AuthFinish(Link l, int which, int ok) { Auth const a = &l->lcp.auth; if (which == AUTH_SELF_TO_PEER) a->self_to_peer = 0; else a->peer_to_self = 0; /* Did auth fail (in either direction)? */ if (!ok) { AuthStop(l); LcpAuthResult(l, FALSE); return; } /* Did auth succeed (in both directions)? */ if (!a->peer_to_self && !a->self_to_peer) { AuthStop(l); LcpAuthResult(l, TRUE); return; } } /* * AuthCleanup() * * Cleanup auth structure, invoked on link-down */ void AuthCleanup(Link l) { Auth a = &l->lcp.auth; Log(LG_AUTH2, ("[%s] AUTH: Cleanup", l->name)); authparamsDestroy(&a->params); l->session_id[0] = 0; } /* * AuthDataNew() * * Create a new auth-data object */ AuthData AuthDataNew(Link l) { AuthData auth; Auth a = &l->lcp.auth; auth = Malloc(MB_AUTH, sizeof(*auth)); auth->reply_message = NULL; auth->mschap_error = NULL; auth->mschapv2resp = NULL; auth->conf = l->lcp.auth.conf; if (l->lcp.auth.conf.extauth_script) auth->conf.extauth_script = Mstrdup(MB_AUTH, l->lcp.auth.conf.extauth_script); if (l->lcp.auth.conf.extacct_script) auth->conf.extacct_script = Mstrdup(MB_AUTH, l->lcp.auth.conf.extacct_script); strlcpy(auth->info.lnkname, l->name, sizeof(auth->info.lnkname)); strlcpy(auth->info.msession_id, l->msession_id, sizeof(auth->info.msession_id)); strlcpy(auth->info.session_id, l->session_id, sizeof(auth->info.session_id)); strlcpy(auth->info.peer_ident, l->lcp.peer_ident, sizeof(l->lcp.peer_ident)); auth->info.originate = l->originate; auth->info.downReason = NULL; if (l->bund) { strlcpy(auth->info.ifname, l->bund->iface.ifname, sizeof(auth->info.ifname)); auth->info.ifindex = l->bund->iface.ifindex; strlcpy(auth->info.bundname, l->bund->name, sizeof(auth->info.bundname)); auth->info.n_links = l->bund->n_links; auth->info.peer_addr = l->bund->ipcp.peer_addr; bcopy(&auth->info.peer_addr6, &l->bund->ipv6cp.peer_addr, sizeof(struct in6_addr)); } /* Copy current link statistics */ memcpy(&auth->info.stats, &l->stats, sizeof(auth->info.stats)); #ifdef USE_NG_BPF /* If it is present copy services statistics */ if (l->bund) { IfaceGetStats(l->bund, &auth->info.ss); IfaceAddStats(&auth->info.ss, &l->bund->iface.prevstats); } #endif if (l->downReasonValid) auth->info.downReason = Mstrdup(MB_AUTH, l->downReason); auth->info.last_up = l->last_up; auth->info.phys_type = l->type; auth->info.linkID = l->id; authparamsCopy(&a->params, &auth->params); return auth; } /* * AuthDataDestroy() * * Destroy authdata */ void AuthDataDestroy(AuthData auth) { authparamsDestroy(&auth->params); Freee(auth->info.downReason); Freee(auth->reply_message); Freee(auth->mschap_error); Freee(auth->mschapv2resp); #ifdef USE_NG_BPF IfaceFreeStats(&auth->info.ss); #endif Freee(auth->conf.extauth_script); Freee(auth->conf.extacct_script); Freee(auth); } /* * AuthStop() * * Stop the authorization process */ void AuthStop(Link l) { Auth a = &l->lcp.auth; TimerStop(&a->timer); PapStop(&a->pap); ChapStop(&a->chap); #ifdef USE_RADIUS EapStop(&a->eap); #endif paction_cancel(&a->thread); } /* * AuthStat() * * Show auth stats */ int AuthStat(Context ctx, int ac, const char *const av[], const void *arg) { Auth const au = &ctx->lnk->lcp.auth; AuthConf const conf = &au->conf; char buf[48], buf2[16]; #if defined(USE_IPFW) || defined(USE_NG_BPF) struct acl *a; #endif IfaceRoute r; #ifdef USE_NG_BPF int k; #endif (void)ac; (void)av; (void)arg; Printf("Configuration:\r\n"); Printf("\tMy authname : %s\r\n", conf->authname); Printf("\tMax-Logins : %u%s\r\n", gMaxLogins, (gMaxLoginsCI ? " CI" : "")); Printf("\tAcct Update : %d\r\n", conf->acct_update); Printf("\t Limit In : %d\r\n", conf->acct_update_lim_recv); Printf("\t Limit Out : %d\r\n", conf->acct_update_lim_xmit); Printf("\tAuth timeout : %d\r\n", conf->timeout); Printf("\tExtAuth script : %s\r\n", conf->extauth_script ? conf->extauth_script : ""); Printf("\tExtAcct script : %s\r\n", conf->extacct_script ? conf->extacct_script : ""); Printf("Auth options\r\n"); OptStat(ctx, &conf->options, gConfList); Printf("Auth Data\r\n"); Printf("\tPeer authname : %s\r\n", au->params.authname); Printf("\tInterface name : %s\r\n", au->params.ifname); #ifdef SIOCSIFDESCR Printf("\tInterface descr.: \"%s\"\r\n", au->params.ifdescr != NULL ? au->params.ifdescr : ""); #endif #ifdef SIOCAIFGROUP Printf("\tInterface group : %s\r\n", au->params.ifgroup); #endif Printf("\tIP range : %s\r\n", (au->params.range_valid) ? u_rangetoa(&au->params.range, buf, sizeof(buf)) : ""); Printf("\tIP pool : %s\r\n", au->params.ippool); Printf("\tDNS : %s %s\r\n", inet_ntop(AF_INET, &au->params.peer_dns[0], buf, sizeof(buf)), inet_ntop(AF_INET, &au->params.peer_dns[1], buf2, sizeof(buf2))); Printf("\tNBNS : %s %s\r\n", inet_ntop(AF_INET, &au->params.peer_nbns[0], buf, sizeof(buf)), inet_ntop(AF_INET, &au->params.peer_nbns[1], buf2, sizeof(buf2))); Printf("\tVJ Compression : %s\r\n", au->params.vjc_enable ? "yes" : "no"); Printf("\tMTU : %u\r\n", au->params.mtu); Printf("\tSession-Timeout : %u\r\n", au->params.session_timeout); Printf("\tIdle-Timeout : %u\r\n", au->params.idle_timeout); Printf("\tAcct-Update : %u\r\n", au->params.acct_update); Printf("\tRoutes :\r\n"); SLIST_FOREACH(r, &au->params.routes, next) { Printf("\t\t%s\r\n", u_rangetoa(&r->dest, buf, sizeof(buf))); } #ifdef USE_IPFW Printf("\tIPFW rules :\r\n"); a = au->params.acl_rule; while (a) { Printf("\t\t%d\t: '%s'\r\n", a->number, a->rule); a = a->next; } Printf("\tIPFW pipes :\r\n"); a = au->params.acl_pipe; while (a) { Printf("\t\t%d\t: '%s'\r\n", a->number, a->rule); a = a->next; } Printf("\tIPFW queues :\r\n"); a = au->params.acl_queue; while (a) { Printf("\t\t%d\t: '%s'\r\n", a->number, a->rule); a = a->next; } Printf("\tIPFW tables :\r\n"); a = au->params.acl_table; while (a) { if (a->number != 0) Printf("\t\t%d\t: '%s'\r\n", a->number, a->rule); else Printf("\t\t#%d\t: '%s'\r\n", a->real_number, a->rule); a = a->next; } #endif /* USE_IPFW */ #ifdef USE_NG_BPF Printf("\tTraffic filters :\r\n"); for (k = 0; k < ACL_FILTERS; k++) { a = au->params.acl_filters[k]; while (a) { Printf("\t%d#%d\t: '%s'\r\n", (k + 1), a->number, a->rule); a = a->next; } } Printf("\tTraffic limits :\r\n"); for (k = 0; k < 2; k++) { a = au->params.acl_limits[k]; while (a) { Printf("\t\t%s#%d%s%s\t: '%s'\r\n", (k ? "out" : "in"), a->number, ((a->name[0]) ? "#" : ""), a->name, a->rule); a = a->next; } } #endif /* USE_NG_BPF */ Printf("\tMS-Domain : %s\r\n", au->params.msdomain); Printf("\tMPPE Types : %s\r\n", AuthMPPEPolicyname(au->params.msoft.policy)); Printf("\tMPPE Policy : %s\r\n", AuthMPPETypesname(au->params.msoft.types, buf, sizeof(buf))); Printf("\tMPPE Keys : %s\r\n", au->params.msoft.has_keys ? "yes" : "no"); return (0); } /* * AuthAccount() * * Accounting stuff, */ void AuthAccountStart(Link l, int type) { Auth const a = &l->lcp.auth; AuthData auth; /* maybe an outstanding thread is running */ if (a->acct_thread) { if (type == AUTH_ACCT_START || type == AUTH_ACCT_STOP) { paction_cancel(&a->acct_thread); } else { Log(LG_AUTH2, ("[%s] ACCT: Accounting thread is already running", l->name)); return; } } LinkUpdateStats(l); if (type == AUTH_ACCT_STOP) { Log(LG_AUTH2, ("[%s] ACCT: Accounting data for user '%s': %lu seconds, %llu octets in, %llu octets out", l->name, a->params.authname, (unsigned long)(time(NULL) - l->last_up), (unsigned long long)l->stats.recvOctets, (unsigned long long)l->stats.xmitOctets)); } if (type == AUTH_ACCT_START) { u_int updateInterval; if (a->params.acct_update > 0) updateInterval = a->params.acct_update; else updateInterval = a->conf.acct_update; if (updateInterval > 0) { /* Save initial statistics. */ memcpy(&a->prev_stats, &l->stats, sizeof(a->prev_stats)); /* Start accounting update timer. */ TimerInit(&a->acct_timer, "AuthAccountTimer", updateInterval * SECONDS, AuthAccountTimeout, l); TimerStartRecurring(&a->acct_timer); } } if (type == AUTH_ACCT_UPDATE) { /* * Suppress sending of accounting update, if byte threshold * is configured, and delta since last update doesn't exceed it. */ u_int lim_recv, lim_xmit; if (a->params.acct_update_lim_recv > 0) lim_recv = a->params.acct_update_lim_recv; else lim_recv = a->conf.acct_update_lim_recv; if (a->params.acct_update_lim_xmit > 0) lim_xmit = a->params.acct_update_lim_xmit; else lim_xmit = a->conf.acct_update_lim_xmit; if (lim_recv > 0 || lim_xmit > 0) { if ((l->stats.recvOctets - a->prev_stats.recvOctets < lim_recv) && (l->stats.xmitOctets - a->prev_stats.xmitOctets < lim_xmit)) { Log(LG_AUTH2, ("[%s] ACCT: Shouldn't send Interim-Update", l->name)); return; } else { /* Save current statistics. */ memcpy(&a->prev_stats, &l->stats, sizeof(a->prev_stats)); } } } if (type == AUTH_ACCT_STOP) { /* Stop accounting update timer if running. */ TimerStop(&a->acct_timer); } if (Enabled(&a->conf.options, AUTH_CONF_RADIUS_ACCT) || #ifdef USE_PAM Enabled(&a->conf.options, AUTH_CONF_PAM_ACCT) || #endif #ifdef USE_SYSTEM Enabled(&a->conf.options, AUTH_CONF_SYSTEM_ACCT) || #endif Enabled(&a->conf.options, AUTH_CONF_EXT_ACCT)) { auth = AuthDataNew(l); auth->acct_type = type; if (paction_start(&a->acct_thread, &gGiantMutex, AuthAccount, AuthAccountFinish, auth) == -1) { Perror("[%s] ACCT: Couldn't start thread", l->name); AuthDataDestroy(auth); } } } /* * AuthAccountTimeout() * * Timer function for accounting updates */ void AuthAccountTimeout(void *arg) { Link l = (Link) arg; Log(LG_AUTH2, ("[%s] ACCT: Time for Accounting Update", l->name)); AuthAccountStart(l, AUTH_ACCT_UPDATE); } /* * AuthAccount() * * Asynchr. accounting handler, called from a paction. * NOTE: Thread safety is needed here */ static void AuthAccount(void *arg) { AuthData const auth = (AuthData) arg; int err = 0; Log(LG_AUTH2, ("[%s] ACCT: Thread started", auth->info.lnkname)); #ifdef USE_RADIUS if (Enabled(&auth->conf.options, AUTH_CONF_RADIUS_ACCT)) err |= RadiusAccount(auth); #endif #ifdef USE_PAM if (Enabled(&auth->conf.options, AUTH_CONF_PAM_ACCT)) err |= AuthPAMAcct(auth); #endif #ifdef USE_SYSTEM if (Enabled(&auth->conf.options, AUTH_CONF_SYSTEM_ACCT)) err |= AuthSystemAcct(auth); #endif if (Enabled(&auth->conf.options, AUTH_CONF_EXT_ACCT)) err |= AuthExternalAcct(auth); if (err != 0 && auth->acct_type == AUTH_ACCT_START && Enabled(&auth->conf.options, AUTH_CONF_ACCT_MANDATORY)) { Log(LG_AUTH, ("[%s] ACCT: Close link due to accounting start error", auth->info.lnkname)); auth->drop_user = 1; } } /* * AuthAccountFinish * * Return point for the accounting thread() */ static void AuthAccountFinish(void *arg, int was_canceled) { AuthData auth = (AuthData) arg; Link l; if (was_canceled) { Log(LG_AUTH2, ("[%s] ACCT: Thread was canceled", auth->info.lnkname)); } else { Log(LG_AUTH2, ("[%s] ACCT: Thread finished normally", auth->info.lnkname)); } #ifdef USE_RADIUS /* Cleanup */ RadiusClose(auth); #endif if (was_canceled) { AuthDataDestroy(auth); return; } l = gLinks[auth->info.linkID]; if (l == NULL) { AuthDataDestroy(auth); return; } if (auth->drop_user && auth->acct_type != AUTH_ACCT_STOP) { Log(LG_AUTH, ("[%s] ACCT: Link close requested by the accounting", l->name)); RecordLinkUpDownReason(NULL, l, 0, STR_MANUALLY, NULL); LinkClose(l); } AuthDataDestroy(auth); LinkShutdownCheck(l, l->lcp.fsm.state); } /* * AuthGetData() * * NOTE: Thread safety is needed here */ int AuthGetData(char *authname, char *password, size_t passlen, struct u_range *range, u_char *range_valid) { FILE *fp; int ac; char *av[20]; char *line; /* Check authname, must be non-empty */ if (authname == NULL || authname[0] == 0) { return (-1); } /* Search secrets file */ if ((fp = OpenConfFile(SECRET_FILE, NULL)) == NULL) return (-1); while ((line = ReadFullLine(fp, NULL, NULL, 0)) != NULL) { memset(av, 0, sizeof(av)); ac = ParseLine(line, av, sizeof(av) / sizeof(*av), 1); Freee(line); if (ac >= 2 && (strcmp(av[0], authname) == 0 || (av[1][0] == '!' && strcmp(av[0], "*") == 0))) { if (av[1][0] == '!') { /* external auth program */ if (AuthGetExternalPassword((av[1] + 1), authname, password, passlen) == -1) { FreeArgs(ac, av); fclose(fp); return (-1); } } else { strlcpy(password, av[1], passlen); } if (range != NULL && range_valid != NULL) { u_rangeclear(range); if (ac >= 3) *range_valid = ParseRange(av[2], range, ALLOW_IPV4); else *range_valid = FALSE; } FreeArgs(ac, av); fclose(fp); return (0); } FreeArgs(ac, av); } fclose(fp); return (-1); /* Invalid */ } /* * AuthAsyncStart() * * Starts the Auth-Thread */ void AuthAsyncStart(Link l, AuthData auth) { Auth const a = &l->lcp.auth; const char *rept; /* Check link action */ rept = LinkMatchAction(l, 2, auth->params.authname); if (rept) { if (strcmp(rept, "##DROP##") == 0) { /* Action told we must drop this connection */ Log(LG_AUTH, ("[%s] Drop connection", l->name)); PhysClose(l); AuthDataDestroy(auth); return; } /* Action told we must forward this connection */ if (RepCreate(l, rept)) { Log(LG_ERR, ("[%s] Repeater to \"%s\" creation error", l->name, rept)); PhysClose(l); AuthDataDestroy(auth); return; } /* Create repeater */ RepIncoming(l); /* Reconnect link netgraph hook to repeater */ LinkNgToRep(l); /* Kill the LCP */ LcpDown(l); LcpClose(l); AuthDataDestroy(auth); return; } /* Check if we are ready to process request. */ if (a->thread) { auth->status = AUTH_STATUS_BUSY; auth->finish(l, auth); return; } /* perform pre authentication checks (single-login, etc.) */ if (AuthPreChecks(auth) < 0) { Log(LG_AUTH, ("[%s] AUTH: AuthPreCheck failed for \"%s\"", l->name, auth->params.authname)); auth->finish(l, auth); return; } if (paction_start(&a->thread, &gGiantMutex, AuthAsync, AuthAsyncFinish, auth) == -1) { Perror("[%s] AUTH: Couldn't start thread", l->name); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; auth->finish(l, auth); } } /* * AuthAsync() * * Asynchr. auth handler, called from a paction. * NOTE: Thread safety is needed here */ static void AuthAsync(void *arg) { AuthData const auth = (AuthData) arg; Log(LG_AUTH2, ("[%s] AUTH: Thread started", auth->info.lnkname)); if (Enabled(&auth->conf.options, AUTH_CONF_EXT_AUTH)) { auth->params.authentic = AUTH_CONF_EXT_AUTH; Log(LG_AUTH, ("[%s] AUTH: Trying EXTERNAL", auth->info.lnkname)); if (AuthExternal(auth)) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: EXTERNAL returned error", auth->info.lnkname)); } else { Log(LG_AUTH, ("[%s] AUTH: EXTERNAL returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS || auth->status == AUTH_STATUS_UNDEF) return; } } #ifdef USE_RADIUS if (auth->proto == PROTO_EAP && auth->eap_radius) { auth->params.authentic = AUTH_CONF_RADIUS_AUTH; RadiusEapProxy(auth); return; } else if (Enabled(&auth->conf.options, AUTH_CONF_RADIUS_AUTH)) { auth->params.authentic = AUTH_CONF_RADIUS_AUTH; Log(LG_AUTH, ("[%s] AUTH: Trying RADIUS", auth->info.lnkname)); if (RadiusAuthenticate(auth)) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: RADIUS returned error", auth->info.lnkname)); } else { Log(LG_AUTH, ("[%s] AUTH: RADIUS returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS) return; } } #endif #ifdef USE_PAM if (Enabled(&auth->conf.options, AUTH_CONF_PAM_AUTH)) { auth->params.authentic = AUTH_CONF_PAM_AUTH; Log(LG_AUTH, ("[%s] AUTH: Trying PAM", auth->info.lnkname)); AuthPAM(auth); Log(LG_AUTH, ("[%s] AUTH: PAM returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS || auth->status == AUTH_STATUS_UNDEF) return; } #endif #ifdef USE_SYSTEM if (Enabled(&auth->conf.options, AUTH_CONF_SYSTEM_AUTH)) { auth->params.authentic = AUTH_CONF_SYSTEM_AUTH; Log(LG_AUTH, ("[%s] AUTH: Trying SYSTEM", auth->info.lnkname)); AuthSystem(auth); Log(LG_AUTH, ("[%s] AUTH: SYSTEM returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS || auth->status == AUTH_STATUS_UNDEF) return; } #endif #ifdef USE_OPIE if (Enabled(&auth->conf.options, AUTH_CONF_OPIE)) { auth->params.authentic = AUTH_CONF_OPIE; Log(LG_AUTH, ("[%s] AUTH: Trying OPIE", auth->info.lnkname)); AuthOpie(auth); Log(LG_AUTH, ("[%s] AUTH: OPIE returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS || auth->status == AUTH_STATUS_UNDEF) return; } #endif /* USE_OPIE */ if (Enabled(&auth->conf.options, AUTH_CONF_INTERNAL)) { auth->params.authentic = AUTH_CONF_INTERNAL; Log(LG_AUTH, ("[%s] AUTH: Trying INTERNAL", auth->info.lnkname)); AuthInternal(auth); Log(LG_AUTH, ("[%s] AUTH: INTERNAL returned: %s", auth->info.lnkname, AuthStatusText(auth->status))); if (auth->status == AUTH_STATUS_SUCCESS || auth->status == AUTH_STATUS_UNDEF) return; } Log(LG_AUTH, ("[%s] AUTH: ran out of backends", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; } /* * AuthAsyncFinish() * * Return point for the auth thread */ static void AuthAsyncFinish(void *arg, int was_canceled) { AuthData auth = (AuthData) arg; Link l; if (was_canceled) Log(LG_AUTH2, ("[%s] AUTH: Thread was canceled", auth->info.lnkname)); #ifdef USE_RADIUS /* cleanup */ RadiusClose(auth); #endif if (was_canceled) { AuthDataDestroy(auth); return; } l = gLinks[auth->info.linkID]; if (l == NULL) { AuthDataDestroy(auth); return; } Log(LG_AUTH2, ("[%s] AUTH: Thread finished normally", l->name)); /* Replace modified data */ authparamsDestroy(&l->lcp.auth.params); authparamsMove(&auth->params, &l->lcp.auth.params); if (strcmp(l->lcp.auth.params.action, "drop") == 0) { auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; } else if (strncmp(l->lcp.auth.params.action, "forward ", 8) == 0) { const char *rept = l->lcp.auth.params.action + 8; /* Action told we must forward this connection */ if (RepCreate(l, rept)) { Log(LG_ERR, ("[%s] Repeater to \"%s\" creation error", l->name, rept)); PhysClose(l); AuthDataDestroy(auth); return; } /* Create repeater */ RepIncoming(l); /* Reconnect link netgraph hook to repeater */ LinkNgToRep(l); /* Kill the LCP */ LcpDown(l); LcpClose(l); AuthDataDestroy(auth); return; } auth->finish(l, auth); } /* * AuthInternal() * * Authenticate against mpd.secret */ static void AuthInternal(AuthData auth) { if (AuthGetData(auth->params.authname, auth->params.password, sizeof(auth->params.password), &auth->params.range, &auth->params.range_valid) < 0) { Log(LG_AUTH, ("[%s] AUTH: User \"%s\" not found in secret file", auth->info.lnkname, auth->params.authname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return; } auth->status = AUTH_STATUS_UNDEF; } #ifdef USE_SYSTEM /* * AuthSystem() * * Authenticate against Systems password database */ static void AuthSystem(AuthData auth) { PapParams pp = &auth->params.pap; struct passwd *pw; struct passwd pwc; u_char *bin; int err; /* * protect getpwnam and errno NOTE: getpwnam_r doesen't exists on * FreeBSD < 5.1 */ GIANT_MUTEX_LOCK(); errno = 0; pw = getpwnam(auth->params.authname); if (!pw) { err = errno; GIANT_MUTEX_UNLOCK(); /* We must release lock before Log() */ if (err) Perror("[%s] AUTH: Error retrieving passwd", auth->info.lnkname); else Log(LG_AUTH, ("[%s] AUTH: User \"%s\" not found in the systems database", auth->info.lnkname, auth->params.authname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return; } memcpy(&pwc, pw, sizeof(struct passwd)); /* we must make copy * before release lock */ GIANT_MUTEX_UNLOCK(); Log(LG_AUTH, ("[%s] AUTH: Found user %s Uid:%d Gid:%d Fmt:%*.*s", auth->info.lnkname, pwc.pw_name, pwc.pw_uid, pwc.pw_gid, 3, 3, pwc.pw_passwd)); if (auth->proto == PROTO_PAP) { /* protect non-ts crypt() */ GIANT_MUTEX_LOCK(); if (strcmp(crypt(pp->peer_pass, pwc.pw_passwd), pwc.pw_passwd) == 0) { auth->status = AUTH_STATUS_SUCCESS; } else { auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; } GIANT_MUTEX_UNLOCK(); return; } else if (auth->proto == PROTO_CHAP && (auth->alg == CHAP_ALG_MSOFT || auth->alg == CHAP_ALG_MSOFTv2)) { if (!strstr(pwc.pw_passwd, "$3$$")) { Log(LG_AUTH, ("[%s] AUTH: Password has the wrong format, nth ($3$) is needed", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return; } bin = Hex2Bin(&pwc.pw_passwd[4]); memcpy(auth->params.msoft.nt_hash, bin, sizeof(auth->params.msoft.nt_hash)); Freee(bin); NTPasswordHashHash(auth->params.msoft.nt_hash, auth->params.msoft.nt_hash_hash); auth->params.msoft.has_nt_hash = TRUE; auth->status = AUTH_STATUS_UNDEF; return; } else { Log(LG_ERR, ("[%s] AUTH: Using systems password database only possible for PAP and MS-CHAP", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; return; } } /* * AuthSystemAcct() * * Account with system */ #if __FreeBSD_version >= 900007 static int AuthSystemAcct(AuthData auth) { struct utmpx ut; memset(&ut, 0, sizeof(ut)); snprintf(ut.ut_id, sizeof(ut.ut_id), "mpd%x", auth->info.linkID); strlcpy(ut.ut_line, auth->info.lnkname, sizeof(ut.ut_line)); if (auth->acct_type == AUTH_ACCT_START) { ut.ut_type = USER_PROCESS; strlcpy(ut.ut_host, auth->params.peeraddr, sizeof(ut.ut_host)); strlcpy(ut.ut_user, auth->params.authname, sizeof(ut.ut_user)); gettimeofday(&ut.ut_tv, NULL); Log(LG_AUTH, ("[%s] ACCT: wtmp %s %s %s login", auth->info.lnkname, ut.ut_line, ut.ut_user, ut.ut_host)); pututxline(&ut); } else if (auth->acct_type == AUTH_ACCT_STOP) { ut.ut_type = DEAD_PROCESS; Log(LG_AUTH, ("[%s] ACCT: wtmp %s logout", auth->info.lnkname, ut.ut_line)); pututxline(&ut); } return (0); } #else static int AuthSystemAcct(AuthData auth) { struct utmp ut; memset(&ut, 0, sizeof(ut)); strlcpy(ut.ut_line, auth->info.lnkname, sizeof(ut.ut_line)); if (auth->acct_type == AUTH_ACCT_START) { time_t t; strlcpy(ut.ut_host, auth->params.peeraddr, sizeof(ut.ut_host)); strlcpy(ut.ut_name, auth->params.authname, sizeof(ut.ut_name)); time(&t); ut.ut_time = t; login(&ut); Log(LG_AUTH, ("[%s] ACCT: wtmp %s %s %s login", auth->info.lnkname, ut.ut_line, ut.ut_name, ut.ut_host)); } else if (auth->acct_type == AUTH_ACCT_STOP) { Log(LG_AUTH, ("[%s] ACCT: wtmp %s logout", auth->info.lnkname, ut.ut_line)); logout(ut.ut_line); logwtmp(ut.ut_line, "", ""); } return (0); } #endif /* __FreeBSD_version >= 900007 */ #endif /* USE_SYSTEM */ #ifdef USE_PAM /* * AuthPAM() * * Authenticate with PAM system */ static int pam_conv(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { AuthData auth = (AuthData) data; int i; for (i = 0; i < n; i++) { Log(LG_AUTH2, ("[%s] AUTH: PAM: %s", auth->info.lnkname, msg[i]->msg)); } /* We support only requests for password */ if (n != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) return (PAM_CONV_ERR); if ((*resp = malloc(sizeof(struct pam_response))) == NULL) return (PAM_CONV_ERR); (*resp)[0].resp = strdup(auth->params.pap.peer_pass); (*resp)[0].resp_retcode = 0; return ((*resp)[0].resp != NULL ? PAM_SUCCESS : PAM_CONV_ERR); } static void AuthPAM(AuthData auth) { struct pam_conv pamc = { &pam_conv, auth }; pam_handle_t *pamh; int status; if (auth->proto != PROTO_PAP) { Log(LG_ERR, ("[%s] AUTH: Using PAM only possible for PAP", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; return; } if (pam_start(gPamService, auth->params.authname, &pamc, &pamh) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] AUTH: PAM error", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; return; } if (auth->params.peeraddr[0] && pam_set_item(pamh, PAM_RHOST, auth->params.peeraddr) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] AUTH: PAM set PAM_RHOST error", auth->info.lnkname)); } if (pam_set_item(pamh, PAM_TTY, auth->info.lnkname) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] AUTH: PAM set PAM_TTY error", auth->info.lnkname)); } status = pam_authenticate(pamh, 0); if (status == PAM_SUCCESS) { status = pam_acct_mgmt(pamh, 0); } if (status == PAM_SUCCESS) { auth->status = AUTH_STATUS_SUCCESS; } else { Log(LG_AUTH, ("[%s] AUTH: PAM error: %s", auth->info.lnkname, pam_strerror(pamh, status))); switch (status) { case PAM_AUTH_ERR: case PAM_USER_UNKNOWN: auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; break; case PAM_ACCT_EXPIRED: case PAM_AUTHTOK_EXPIRED: case PAM_CRED_EXPIRED: auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_ACCT_DISABLED; break; default: auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; } } pam_end(pamh, status); } /* * AuthPAMAcct() * * Account with system */ static int AuthPAMAcct(AuthData auth) { pam_handle_t *pamh; int status; struct pam_conv pamc = { &pam_conv, auth }; if (auth->acct_type != AUTH_ACCT_START && auth->acct_type != AUTH_ACCT_STOP) { return (0); } if (pam_start(gPamService, auth->params.authname, &pamc, &pamh) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] ACCT: PAM error", auth->info.lnkname)); return (-1); } if (auth->params.peeraddr[0] && pam_set_item(pamh, PAM_RHOST, auth->params.peeraddr) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] ACCT: PAM set PAM_RHOST error", auth->info.lnkname)); return (-1); } if (pam_set_item(pamh, PAM_TTY, auth->info.lnkname) != PAM_SUCCESS) { Log(LG_ERR, ("[%s] ACCT: PAM set PAM_TTY error", auth->info.lnkname)); return (-1); } if (auth->acct_type == AUTH_ACCT_START) { Log(LG_AUTH, ("[%s] ACCT: PAM open session \"%s\"", auth->info.lnkname, auth->params.authname)); status = pam_open_session(pamh, 0); } else { Log(LG_AUTH, ("[%s] ACCT: PAM close session \"%s\"", auth->info.lnkname, auth->params.authname)); status = pam_close_session(pamh, 0); } if (status != PAM_SUCCESS) { Log(LG_AUTH, ("[%s] ACCT: PAM session error", auth->info.lnkname)); return (-1); } pam_end(pamh, status); return (0); } #endif /* USE_PAM */ #ifdef USE_OPIE /* * AuthOpie() */ static void AuthOpie(AuthData auth) { PapParams const pp = &auth->params.pap; struct opie_otpkey key; char opieprompt[OPIE_CHALLENGE_MAX + 1]; int ret, n; char secret[OPIE_SECRET_MAX + 1]; char english[OPIE_RESPONSE_MAX + 1]; ret = opiechallenge(&auth->opie.data, auth->params.authname, opieprompt); auth->status = AUTH_STATUS_UNDEF; switch (ret) { case 0: break; case 1: Log(LG_ERR, ("[%s] AUTH: User \"%s\" not found in opiekeys", auth->info.lnkname, auth->params.authname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return; case -1: case 2: default: Log(LG_ERR, ("[%s] AUTH: System error", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_NOT_EXPECTED; return; }; Log(LG_AUTH, ("[%s] AUTH: Opieprompt:%s", auth->info.lnkname, opieprompt)); if (auth->proto == PROTO_PAP) { if (!opieverify(&auth->opie.data, pp->peer_pass)) { auth->status = AUTH_STATUS_SUCCESS; } else { auth->why_fail = AUTH_FAIL_INVALID_LOGIN; auth->status = AUTH_STATUS_FAIL; } return; } if (AuthGetData(auth->params.authname, secret, sizeof(secret), NULL, NULL) < 0) { Log(LG_AUTH, ("[%s] AUTH: Can't get credentials for \"%s\"", auth->info.lnkname, auth->params.authname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return; } opiekeycrunch(OPIE_ALG_MD5, &key, auth->opie.data.opie_seed, secret); n = auth->opie.data.opie_n - 1; while (n-- > 0) opiehash(&key, OPIE_ALG_MD5); opiebtoe(english, &key); strlcpy(auth->params.password, english, sizeof(auth->params.password)); } #endif /* USE_OPIE */ /* * AuthPreChecks() */ static int AuthPreChecks(AuthData auth) { if (!strlen(auth->params.authname)) { Log(LG_AUTH, ("[%s] AUTH: We don't accept empty usernames", auth->info.lnkname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return (-1); } /* check max. number of logins */ if (gMaxLogins != 0) { int ac; u_long num = 0; for (ac = 0; ac < gNumBundles; ac++) { if (gBundles[ac] && gBundles[ac]->open) { if (gMaxLoginsCI) { if (!strcasecmp(gBundles[ac]->params.authname, auth->params.authname)) num++; } else { if (!strcmp(gBundles[ac]->params.authname, auth->params.authname)) num++; } } } if (num >= gMaxLogins) { Log(LG_ERR | LG_AUTH, ("[%s] AUTH: Name: \"%s\" max. number of logins exceeded", auth->info.lnkname, auth->params.authname)); auth->status = AUTH_STATUS_FAIL; auth->why_fail = AUTH_FAIL_INVALID_LOGIN; return (-1); } } return (0); } /* * AuthTimeout() * * Timer expired for the whole authorization process */ static void AuthTimeout(void *arg) { Link l = (Link) arg; Log(LG_AUTH, ("[%s] %s: authorization timer expired", Pref(&l->lcp.fsm), Fsm(&l->lcp.fsm))); AuthStop(l); LcpAuthResult(l, FALSE); } /* * AuthFailMsg() */ const char * AuthFailMsg(AuthData auth, char *buf, size_t len) { const char *mesg; if (auth->proto == PROTO_CHAP && (auth->alg == CHAP_ALG_MSOFT || auth->alg == CHAP_ALG_MSOFTv2)) { int mscode; if (auth->mschap_error != NULL) { strlcpy(buf, auth->mschap_error, len); return (buf); } switch (auth->why_fail) { case AUTH_FAIL_ACCT_DISABLED: mscode = MSCHAP_ERROR_ACCT_DISABLED; mesg = AUTH_MSG_ACCT_DISAB; break; case AUTH_FAIL_NO_PERMISSION: mscode = MSCHAP_ERROR_NO_DIALIN_PERMISSION; mesg = AUTH_MSG_NOT_ALLOWED; break; case AUTH_FAIL_RESTRICTED_HOURS: mscode = MSCHAP_ERROR_RESTRICTED_LOGON_HOURS; mesg = AUTH_MSG_RESTR_HOURS; break; case AUTH_FAIL_INVALID_PACKET: case AUTH_FAIL_INVALID_LOGIN: case AUTH_FAIL_NOT_EXPECTED: default: mscode = MSCHAP_ERROR_AUTHENTICATION_FAILURE; mesg = AUTH_MSG_INVALID; break; } /* If we have reply message, send it instead of default. */ if (auth->reply_message != NULL) mesg = auth->reply_message; snprintf(buf, len, "E=%d R=0 M=%s", mscode, mesg); } else { if (auth->reply_message != NULL) { strlcpy(buf, auth->reply_message, len); return (buf); } switch (auth->why_fail) { case AUTH_FAIL_ACCT_DISABLED: mesg = AUTH_MSG_ACCT_DISAB; break; case AUTH_FAIL_NO_PERMISSION: mesg = AUTH_MSG_NOT_ALLOWED; break; case AUTH_FAIL_RESTRICTED_HOURS: mesg = AUTH_MSG_RESTR_HOURS; break; case AUTH_FAIL_NOT_EXPECTED: mesg = AUTH_MSG_NOT_EXPECTED; break; case AUTH_FAIL_INVALID_PACKET: mesg = AUTH_MSG_BAD_PACKET; break; case AUTH_FAIL_INVALID_LOGIN: default: mesg = AUTH_MSG_INVALID; break; } strlcpy(buf, mesg, len); } return (buf); } /* * AuthStatusText() */ const char * AuthStatusText(int status) { switch (status) { case AUTH_STATUS_UNDEF: return "undefined"; case AUTH_STATUS_SUCCESS: return "authenticated"; case AUTH_STATUS_FAIL: return "failed"; case AUTH_STATUS_BUSY: return "busy"; default: return "INCORRECT STATUS"; } } /* * AuthMPPEPolicyname() */ const char * AuthMPPEPolicyname(int policy) { switch (policy) { case MPPE_POLICY_ALLOWED: return "Allowed"; case MPPE_POLICY_REQUIRED: return "Required"; case MPPE_POLICY_NONE: return "Not available"; default: return "Unknown Policy"; } } /* * AuthMPPETypesname() */ const char * AuthMPPETypesname(int types, char *buf, size_t len) { if (types == 0) { strlcpy(buf, "no encryption required", len); return (buf); } buf[0] = 0; if (types & MPPE_TYPE_40BIT) strlcpy(buf, "40 ", len); if (types & MPPE_TYPE_56BIT) strlcat(buf, "56 ", len); if (types & MPPE_TYPE_128BIT) strlcat(buf, "128 ", len); if (strlen(buf) == 0) { strlcpy(buf, "unknown types", len); } else { strlcat(buf, "bit", len); } return (buf); } /* * AuthGetExternalPassword() * * Run the named external program to fill in the password for the user * mentioned in the AuthData * -1 on error (can't fork, no data read, whatever) */ static int AuthGetExternalPassword(const char *extcmd, char *authname, char *password, size_t passlen) { char cmd[AUTH_MAX_PASSWORD + 5 + AUTH_MAX_AUTHNAME]; int ok = 0; FILE *fp; int len; snprintf(cmd, sizeof(cmd), "%s %s", extcmd, authname); Log(LG_AUTH, ("Invoking external auth program: '%s'", cmd)); if ((fp = popen(cmd, "r")) == NULL) { Perror("Popen"); return (-1); } if (fgets(password, passlen, fp) != NULL) { len = strlen(password); /* trim trailing newline */ if (len > 0 && password[len - 1] == '\n') password[len - 1] = '\0'; ok = (password[0] != '\0'); } else { if (ferror(fp)) Perror("Error reading from external auth program"); } if (!ok) Log(LG_AUTH, ("External auth program failed for user \"%s\"", authname)); pclose(fp); return (ok ? 0 : -1); } /* * AuthCode() */ static const char * AuthCode(int proto, u_char code, char *buf, size_t len) { switch (proto) { #ifdef USE_RADIUS case PROTO_EAP: return EapCode(code, buf, len); #endif case PROTO_CHAP: return ChapCode(code, buf, len); case PROTO_PAP: return PapCode(code, buf, len); default: snprintf(buf, len, "code %d", code); return (buf); } } /* * AuthSetCommand() */ static int AuthSetCommand(Context ctx, int ac, const char *const av[], const void *arg) { AuthConf const autc = &ctx->lnk->lcp.auth.conf; int val; if (ac == 0) return (-1); switch ((intptr_t)arg) { case SET_AUTHNAME: strlcpy(autc->authname, *av, sizeof(autc->authname)); break; case SET_PASSWORD: strlcpy(autc->password, *av, sizeof(autc->password)); break; case SET_EXTAUTH_SCRIPT: Freee(autc->extauth_script); autc->extauth_script = Mstrdup(MB_AUTH, *av); break; case SET_EXTACCT_SCRIPT: Freee(autc->extacct_script); autc->extacct_script = Mstrdup(MB_AUTH, *av); break; case SET_MAX_LOGINS: gMaxLogins = (unsigned)atoi(av[0]); if (ac >= 2 && strcasecmp(av[1], "ci") == 0) { gMaxLoginsCI = 1; } else { gMaxLoginsCI = 0; } break; case SET_ACCT_UPDATE: val = atoi(*av); if (val < 0) Error("Update interval must be positive."); else autc->acct_update = val; break; case SET_ACCT_UPDATE_LIMIT_IN: case SET_ACCT_UPDATE_LIMIT_OUT: val = atoi(*av); if (val < 0) Error("Update suppression limit must be positive."); else { if ((intptr_t)arg == SET_ACCT_UPDATE_LIMIT_IN) autc->acct_update_lim_recv = val; else autc->acct_update_lim_xmit = val; } break; case SET_TIMEOUT: val = atoi(*av); if (val <= 20) Error("Authorization timeout must be greater then 20."); else autc->timeout = val; break; case SET_ACCEPT: AcceptCommand(ac, av, &autc->options, gConfList); break; case SET_DENY: DenyCommand(ac, av, &autc->options, gConfList); break; case SET_ENABLE: EnableCommand(ac, av, &autc->options, gConfList); break; case SET_DISABLE: DisableCommand(ac, av, &autc->options, gConfList); break; case SET_YES: YesCommand(ac, av, &autc->options, gConfList); break; case SET_NO: NoCommand(ac, av, &autc->options, gConfList); break; default: assert(0); } return (0); } /* * AuthExternal() * * Authenticate via call external script extauth-script */ static int AuthExternal(AuthData auth) { char line[256]; FILE *fp; char *attr, *val; int len; if (!auth->conf.extauth_script || !auth->conf.extauth_script[0]) { Log(LG_ERR, ("[%s] Ext-auth: Script not specified!", auth->info.lnkname)); return (-1); } if (strchr(auth->params.authname, '\'') || strchr(auth->params.authname, '\n')) { Log(LG_ERR, ("[%s] Ext-auth: Denied character in USER_NAME!", auth->info.lnkname)); return (-1); } snprintf(line, sizeof(line), "%s '%s'", auth->conf.extauth_script, auth->params.authname); Log(LG_AUTH, ("[%s] Ext-auth: Invoking auth program: '%s'", auth->info.lnkname, line)); if ((fp = popen(line, "r+")) == NULL) { Perror("Popen"); return (-1); } /* SENDING REQUEST */ fprintf(fp, "USER_NAME:%s\n", auth->params.authname); fprintf(fp, "AUTH_TYPE:%s", ProtoName(auth->proto)); if (auth->proto == PROTO_CHAP) { switch (auth->alg) { case CHAP_ALG_MD5: fprintf(fp, " MD5\n"); break; case CHAP_ALG_MSOFT: fprintf(fp, " MSOFT\n"); break; case CHAP_ALG_MSOFTv2: fprintf(fp, " MSOFTv2\n"); break; default: fprintf(fp, " 0x%02x\n", auth->alg); break; } } else fprintf(fp, "\n"); if (auth->proto == PROTO_PAP) fprintf(fp, "USER_PASSWORD:%s\n", auth->params.pap.peer_pass); fprintf(fp, "ACCT_SESSION_ID:%s\n", auth->info.session_id); fprintf(fp, "LINK:%s\n", auth->info.lnkname); fprintf(fp, "NAS_PORT:%d\n", auth->info.linkID); fprintf(fp, "NAS_PORT_TYPE:%s\n", auth->info.phys_type->name); fprintf(fp, "CALLING_STATION_ID:%s\n", auth->params.callingnum); fprintf(fp, "CALLED_STATION_ID:%s\n", auth->params.callednum); fprintf(fp, "SELF_NAME:%s\n", auth->params.selfname); fprintf(fp, "PEER_NAME:%s\n", auth->params.peername); fprintf(fp, "SELF_ADDR:%s\n", auth->params.selfaddr); fprintf(fp, "PEER_ADDR:%s\n", auth->params.peeraddr); fprintf(fp, "PEER_PORT:%s\n", auth->params.peerport); fprintf(fp, "PEER_MAC_ADDR:%s\n", auth->params.peermacaddr); fprintf(fp, "PEER_IFACE:%s\n", auth->params.peeriface); fprintf(fp, "PEER_IDENT:%s\n", auth->info.peer_ident); /* REQUEST DONE */ fprintf(fp, "\n"); /* REPLY PROCESSING */ auth->status = AUTH_STATUS_FAIL; while (fgets(line, sizeof(line), fp)) { /* trim trailing newline */ len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } /* Empty line is the end marker */ if (len == 0) break; /* split line on attr:value */ val = line; attr = strsep(&val, ":"); /* Log data w/o password */ if (strcmp(attr, "USER_PASSWORD") != 0) { Log(LG_AUTH2, ("[%s] Ext-auth: attr:'%s', value:'%s'", auth->info.lnkname, attr, val)); } else { Log(LG_AUTH2, ("[%s] Ext-auth: attr:'%s', value:'XXX'", auth->info.lnkname, attr)); } if (strcmp(attr, "RESULT") == 0) { if (strcmp(val, "SUCCESS") == 0) { auth->status = AUTH_STATUS_SUCCESS; } else if (strcmp(val, "UNDEF") == 0) { auth->status = AUTH_STATUS_UNDEF; } else auth->status = AUTH_STATUS_FAIL; } else if (strcmp(attr, "USER_NAME") == 0) { strlcpy(auth->params.authname, val, sizeof(auth->params.authname)); } else if (strcmp(attr, "USER_PASSWORD") == 0) { strlcpy(auth->params.password, val, sizeof(auth->params.password)); } else if (strcmp(attr, "USER_NT_HASH") == 0) { if (strlen(val) != 32) { Log(LG_AUTH, ("[%s] Ext-auth: Incorrect USER_NT_HASH length", auth->info.lnkname)); } else { u_char *bin = Hex2Bin(val); memcpy(auth->params.msoft.nt_hash, bin, sizeof(auth->params.msoft.nt_hash)); Freee(bin); NTPasswordHashHash(auth->params.msoft.nt_hash, auth->params.msoft.nt_hash_hash); auth->params.msoft.has_nt_hash = TRUE; } } else if (strcmp(attr, "USER_LM_HASH") == 0) { if (strlen(val) != 32) { Log(LG_AUTH, ("[%s] Ext-auth: Incorrect USER_LM_HASH length", auth->info.lnkname)); } else { u_char *bin = Hex2Bin(val); memcpy(auth->params.msoft.lm_hash, bin, sizeof(auth->params.msoft.lm_hash)); Freee(bin); auth->params.msoft.has_lm_hash = TRUE; } } else if (strcmp(attr, "FRAMED_IP_ADDRESS") == 0) { auth->params.range_valid = ParseRange(val, &auth->params.range, ALLOW_IPV4); } else if (strcmp(attr, "PRIMARY_DNS_SERVER") == 0) { inet_pton(AF_INET, val, &auth->params.peer_dns[0]); } else if (strcmp(attr, "SECONDARY_DNS_SERVER") == 0) { inet_pton(AF_INET, val, &auth->params.peer_dns[1]); } else if (strcmp(attr, "PRIMARY_NBNS_SERVER") == 0) { inet_pton(AF_INET, val, &auth->params.peer_nbns[0]); } else if (strcmp(attr, "SECONDARY_NBNS_SERVER") == 0) { inet_pton(AF_INET, val, &auth->params.peer_nbns[1]); } else if (strcmp(attr, "FRAMED_ROUTE") == 0) { struct u_range range; if (!ParseRange(val, &range, ALLOW_IPV4)) { Log(LG_ERR | LG_AUTH, ("[%s] Ext-auth: FRAMED_ROUTE: Bad route \"%s\"", auth->info.lnkname, val)); } else { struct ifaceroute *r, *r1; int j; r = Malloc(MB_AUTH, sizeof(struct ifaceroute)); r->dest = range; r->ok = 0; j = 0; SLIST_FOREACH(r1, &auth->params.routes, next) { if (!u_rangecompare(&r->dest, &r1->dest)) { Log(LG_ERR | LG_AUTH, ("[%s] Ext-auth: Duplicate route", auth->info.lnkname)); j = 1; } }; if (j == 0) { SLIST_INSERT_HEAD(&auth->params.routes, r, next); } else { Freee(r); } } } else if (strcmp(attr, "FRAMED_IPV6_ROUTE") == 0) { struct u_range range; if (!ParseRange(val, &range, ALLOW_IPV6)) { Log(LG_ERR | LG_AUTH, ("[%s] Ext-auth: FRAMED_IPV6_ROUTE: Bad route \"%s\"", auth->info.lnkname, val)); } else { struct ifaceroute *r, *r1; int j; r = Malloc(MB_AUTH, sizeof(struct ifaceroute)); r->dest = range; r->ok = 0; j = 0; SLIST_FOREACH(r1, &auth->params.routes, next) { if (!u_rangecompare(&r->dest, &r1->dest)) { Log(LG_ERR | LG_AUTH, ("[%s] Ext-auth: Duplicate route", auth->info.lnkname)); j = 1; } }; if (j == 0) { SLIST_INSERT_HEAD(&auth->params.routes, r, next); } else { Freee(r); } } } else if (strcmp(attr, "SESSION_TIMEOUT") == 0) { auth->params.session_timeout = atoi(val); } else if (strcmp(attr, "IDLE_TIMEOUT") == 0) { auth->params.idle_timeout = atoi(val); } else if (strcmp(attr, "ACCT_INTERIM_INTERVAL") == 0) { auth->params.acct_update = atoi(val); } else if (strcmp(attr, "ACCT_INTERIM_LIM_RECV") == 0) { auth->params.acct_update_lim_recv = atoi(val); } else if (strcmp(attr, "ACCT_INTERIM_LIM_XMIT") == 0) { auth->params.acct_update_lim_xmit = atoi(val); } else if (strcmp(attr, "FRAMED_MTU") == 0) { auth->params.mtu = atoi(val); } else if (strcmp(attr, "FRAMED_COMPRESSION") == 0) { if (atoi(val) == 1) auth->params.vjc_enable = 1; } else if (strcmp(attr, "FRAMED_POOL") == 0) { strlcpy(auth->params.ippool, val, sizeof(auth->params.ippool)); } else if (strcmp(attr, "REPLY_MESSAGE") == 0) { Freee(auth->reply_message); auth->reply_message = Mstrdup(MB_AUTH, val); } else if (strcmp(attr, "MS_CHAP_ERROR") == 0) { Freee(auth->mschap_error); /* "E=%d R=0 M=%s" */ auth->mschap_error = Mstrdup(MB_AUTH, val); } else if (strcmp(attr, "MPD_ACTION") == 0) { strlcpy(auth->params.action, val, sizeof(auth->params.action)); } else if (strcmp(attr, "MPD_IFACE_NAME") == 0) { strlcpy(auth->params.ifname, val, sizeof(auth->params.ifname)); #ifdef SIOCSIFDESCR } else if (strcmp(attr, "MPD_IFACE_DESCR") == 0) { Freee(auth->params.ifdescr); auth->params.ifdescr = Mstrdup(MB_AUTH, val); #endif /* SIOCSIFDESCR */ #ifdef SIOCAIFGROUP } else if (strcmp(attr, "MPD_IFACE_GROUP") == 0) { strlcpy(auth->params.ifgroup, val, sizeof(auth->params.ifgroup)); #endif #if defined(USE_IPFW) || defined(USE_NG_BPF) } else if (strncmp(attr, "MPD_", 4) == 0) { struct acl **acls, *acls1; char *acl1, *acl2, *acl3; int i; acl1 = NULL; acls = NULL; #ifdef USE_IPFW if (strcmp(attr, "MPD_RULE") == 0) { acl1 = val; acls = &(auth->params.acl_rule); } else if (strcmp(attr, "MPD_PIPE") == 0) { acl1 = val; acls = &(auth->params.acl_pipe); } else if (strcmp(attr, "MPD_QUEUE") == 0) { acl1 = val; acls = &(auth->params.acl_queue); } else if (strcmp(attr, "MPD_TABLE") == 0) { acl1 = val; acls = &(auth->params.acl_table); } else if (strcmp(attr, "MPD_TABLE_STATIC") == 0) { acl1 = val; acls = &(auth->params.acl_table); } else #endif /* USE_IPFW */ #ifdef USE_NG_BPF if (strcmp(attr, "MPD_FILTER") == 0) { acl1 = val; acl2 = strsep(&acl1, "#"); i = atol(acl2); if (i <= 0 || i > ACL_FILTERS) { Log(LG_ERR, ("[%s] Ext-auth: wrong filter number: %i", auth->info.lnkname, i)); continue; } acls = &(auth->params.acl_filters[i - 1]); } else if (strcmp(attr, "MPD_LIMIT") == 0) { acl1 = val; acl2 = strsep(&acl1, "#"); if (strcasecmp(acl2, "in") == 0) { i = 0; } else if (strcasecmp(acl2, "out") == 0) { i = 1; } else { Log(LG_ERR, ("[%s] Ext-auth: wrong limit direction: '%s'", auth->info.lnkname, acl2)); continue; } acls = &(auth->params.acl_limits[i]); } else { Log(LG_ERR, ("[%s] Ext-auth: Dropping MPD vendor specific attribute: '%s'", auth->info.lnkname, attr)); continue; } #endif /* USE_NG_BPF */ if (acl1 == NULL) { Log(LG_ERR, ("[%s] Ext-auth: incorrect acl!", auth->info.lnkname)); continue; } acl3 = acl1; strsep(&acl3, "="); acl2 = acl1; strsep(&acl2, "#"); i = atol(acl1); if (i <= 0) { Log(LG_ERR, ("[%s] Ext-auth: wrong acl number: %i", auth->info.lnkname, i)); continue; } if ((acl3 == NULL) || (acl3[0] == 0)) { Log(LG_ERR, ("[%s] Ext-auth: wrong acl", auth->info.lnkname)); continue; } acls1 = Malloc(MB_AUTH, sizeof(struct acl) + strlen(acl3)); if (strcmp(attr, "MPD_TABLE_STATIC") != 0) { acls1->number = i; acls1->real_number = 0; } else { acls1->number = 0; acls1->real_number = i; } if (acl2) strlcpy(acls1->name, acl2, sizeof(acls1->name)); strcpy(acls1->rule, acl3); while ((*acls != NULL) && ((*acls)->number < acls1->number)) acls = &((*acls)->next); if (*acls == NULL) { acls1->next = NULL; } else if (((*acls)->number == acls1->number) && (strcmp(attr, "MPD_TABLE") != 0) && (strcmp(attr, "MPD_TABLE_STATIC") != 0)) { Log(LG_ERR, ("[%s] Ext-auth: duplicate acl", auth->info.lnkname)); continue; } else { acls1->next = *acls; } *acls = acls1; #endif /* USE_IPFW or USE_NG_BPF */ } else { Log(LG_ERR, ("[%s] Ext-auth: Unknown attr:'%s'", auth->info.lnkname, attr)); } } pclose(fp); return (0); } /* * AuthExternalAcct() * * Accounting via call external script extacct-script */ static int AuthExternalAcct(AuthData auth) { char line[256]; FILE *fp; char *attr, *val; int len; if (!auth->conf.extacct_script || !auth->conf.extacct_script[0]) { Log(LG_ERR, ("[%s] Ext-acct: Script not specified!", auth->info.lnkname)); return (-1); } if (strchr(auth->params.authname, '\'') || strchr(auth->params.authname, '\n')) { Log(LG_ERR, ("[%s] Ext-acct: Denied character in USER_NAME!", auth->info.lnkname)); return (-1); } snprintf(line, sizeof(line), "%s '%s'", auth->conf.extacct_script, auth->params.authname); Log(LG_AUTH, ("[%s] Ext-acct: Invoking acct program: '%s'", auth->info.lnkname, line)); if ((fp = popen(line, "r+")) == NULL) { Perror("Popen"); return (-1); } /* SENDING REQUEST */ fprintf(fp, "ACCT_STATUS_TYPE:%s\n", (auth->acct_type == AUTH_ACCT_START) ? "START" : ((auth->acct_type == AUTH_ACCT_STOP) ? "STOP" : "UPDATE")); fprintf(fp, "ACCT_SESSION_ID:%s\n", auth->info.session_id); fprintf(fp, "ACCT_MULTI_SESSION_ID:%s\n", auth->info.msession_id); fprintf(fp, "USER_NAME:%s\n", auth->params.authname); fprintf(fp, "IFACE:%s\n", auth->info.ifname); fprintf(fp, "IFACE_INDEX:%d\n", auth->info.ifindex); fprintf(fp, "BUNDLE:%s\n", auth->info.bundname); fprintf(fp, "LINK:%s\n", auth->info.lnkname); fprintf(fp, "NAS_PORT:%d\n", auth->info.linkID); fprintf(fp, "NAS_PORT_TYPE:%s\n", auth->info.phys_type->name); fprintf(fp, "ACCT_LINK_COUNT:%d\n", auth->info.n_links); fprintf(fp, "CALLING_STATION_ID:%s\n", auth->params.callingnum); fprintf(fp, "CALLED_STATION_ID:%s\n", auth->params.callednum); fprintf(fp, "SELF_NAME:%s\n", auth->params.selfname); fprintf(fp, "PEER_NAME:%s\n", auth->params.peername); fprintf(fp, "SELF_ADDR:%s\n", auth->params.selfaddr); fprintf(fp, "PEER_ADDR:%s\n", auth->params.peeraddr); fprintf(fp, "PEER_PORT:%s\n", auth->params.peerport); fprintf(fp, "PEER_MAC_ADDR:%s\n", auth->params.peermacaddr); fprintf(fp, "PEER_IFACE:%s\n", auth->params.peeriface); fprintf(fp, "PEER_IDENT:%s\n", auth->info.peer_ident); fprintf(fp, "FRAMED_IP_ADDRESS:%s\n", inet_ntoa(auth->info.peer_addr)); if (auth->acct_type == AUTH_ACCT_STOP) fprintf(fp, "ACCT_TERMINATE_CAUSE:%s\n", auth->info.downReason); if (auth->acct_type != AUTH_ACCT_START) { #ifdef USE_NG_BPF struct svcstatrec *ssr; #endif fprintf(fp, "ACCT_SESSION_TIME:%ld\n", (long int)(time(NULL) - auth->info.last_up)); fprintf(fp, "ACCT_INPUT_OCTETS:%llu\n", (long long unsigned)auth->info.stats.recvOctets); fprintf(fp, "ACCT_INPUT_PACKETS:%llu\n", (long long unsigned)auth->info.stats.recvFrames); fprintf(fp, "ACCT_OUTPUT_OCTETS:%llu\n", (long long unsigned)auth->info.stats.xmitOctets); fprintf(fp, "ACCT_OUTPUT_PACKETS:%llu\n", (long long unsigned)auth->info.stats.xmitFrames); #ifdef USE_NG_BPF SLIST_FOREACH(ssr, &auth->info.ss.stat[0], next) { fprintf(fp, "MPD_INPUT_OCTETS:%s:%llu\n", ssr->name, (long long unsigned)ssr->Octets); fprintf(fp, "MPD_INPUT_PACKETS:%s:%llu\n", ssr->name, (long long unsigned)ssr->Packets); } SLIST_FOREACH(ssr, &auth->info.ss.stat[1], next) { fprintf(fp, "MPD_OUTPUT_OCTETS:%s:%llu\n", ssr->name, (long long unsigned)ssr->Octets); fprintf(fp, "MPD_OUTPUT_PACKETS:%s:%llu\n", ssr->name, (long long unsigned)ssr->Packets); } #endif /* USE_NG_BPF */ } /* REQUEST DONE */ fprintf(fp, "\n"); /* REPLY PROCESSING */ while (fgets(line, sizeof(line), fp)) { /* trim trailing newline */ len = strlen(line); if (len > 0 && line[len - 1] == '\n') { line[len - 1] = '\0'; len--; } /* Empty line is the end marker */ if (len == 0) break; /* split line on attr:value */ val = line; attr = strsep(&val, ":"); Log(LG_AUTH2, ("[%s] Ext-acct: attr:'%s', value:'%s'", auth->info.lnkname, attr, val)); if (strcmp(attr, "MPD_DROP_USER") == 0) { auth->drop_user = atoi(val); } else { Log(LG_ERR, ("[%s] Ext-acct: Unknown attr:'%s'", auth->info.lnkname, attr)); } } pclose(fp); return (0); }