/* $OpenBSD: npppd_auth.c,v 1.23 2024/02/26 10:42:05 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /**@file authentication realm */ /* $Id: npppd_auth.c,v 1.23 2024/02/26 10:42:05 yasuoka Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugutil.h" #include "npppd_local.h" #include "npppd_auth.h" #include "net_utils.h" #include "npppd_auth_local.h" #include "npppd_radius.h" /** * Create a npppd_auth_base object. * @param auth_type the authentication type. * specify {@link ::NPPPD_AUTH_TYPE_LOCAL} to authenticate by the local * file, or specify {@link ::NPPPD_AUTH_TYPE_RADIUS} for RADIUS * authentication. * @param name the configuration name * @param _npppd the parent {@link ::npppd} object * @see ::NPPPD_AUTH_TYPE_LOCAL * @see ::NPPPD_AUTH_TYPE_RADIUS * @return The pointer to the {@link ::npppd_auth_base} object will be returned * in case success otherwise NULL will be returned. */ npppd_auth_base * npppd_auth_create(int auth_type, const char *name, void *_npppd) { npppd_auth_base *base; NPPPD_AUTH_ASSERT(name != NULL); switch (auth_type) { case NPPPD_AUTH_TYPE_LOCAL: if ((base = calloc(1, sizeof(npppd_auth_local))) != NULL) { base->type = NPPPD_AUTH_TYPE_LOCAL; strlcpy(base->name, name, sizeof(base->name)); base->npppd = _npppd; return base; } break; #ifdef USE_NPPPD_RADIUS case NPPPD_AUTH_TYPE_RADIUS: if ((base = calloc(1, sizeof(npppd_auth_radius))) != NULL) { npppd_auth_radius *_this = (npppd_auth_radius *)base; base->type = NPPPD_AUTH_TYPE_RADIUS; strlcpy(base->name, name, sizeof(base->name)); base->npppd = _npppd; if ((_this->rad_auth_setting = radius_req_setting_create()) == NULL) goto radius_fail; if ((_this->rad_acct_setting = radius_req_setting_create()) == NULL) goto radius_fail; return base; radius_fail: if (_this->rad_auth_setting != NULL) radius_req_setting_destroy( _this->rad_auth_setting); if (_this->rad_acct_setting != NULL) radius_req_setting_destroy( _this->rad_acct_setting); free(base); return NULL; } break; #endif default: NPPPD_AUTH_ASSERT(0); break; } return NULL; } /** * Call this function to make the object unusable. *

* {@link ::npppd_auth_base} objects is referred by the {@link ::npppd_ppp} * object. After this function is called, npppd will disconnect the PPP * links that refers the object, it will call {@link ::npppd_auth_destroy()} * when all the references to the object are released.

*/ void npppd_auth_dispose(npppd_auth_base *base) { base->disposing = 1; return; } /** Destroy the {@link ::npppd_auth_base} object. */ void npppd_auth_destroy(npppd_auth_base *base) { if (base->disposing == 0) npppd_auth_dispose(base); npppd_auth_base_log(base, LOG_INFO, "Finalized"); switch(base->type) { case NPPPD_AUTH_TYPE_LOCAL: memset(base, 0, sizeof(npppd_auth_local)); break; #ifdef USE_NPPPD_RADIUS case NPPPD_AUTH_TYPE_RADIUS: { npppd_auth_radius *_this = (npppd_auth_radius *)base; if (_this->rad_auth_setting != NULL) radius_req_setting_destroy(_this->rad_auth_setting); _this->rad_auth_setting = NULL; if (_this->rad_acct_setting != NULL) radius_req_setting_destroy(_this->rad_acct_setting); _this->rad_acct_setting = NULL; memset(base, 0, sizeof(npppd_auth_local)); break; } #endif } free(base); return; } /** Reload the configuration */ int npppd_auth_reload(npppd_auth_base *base) { struct authconf *auth; TAILQ_FOREACH(auth, &base->npppd->conf.authconfs, entry) { if (strcmp(auth->name, base->name) == 0) break; } if (auth == NULL) return 1; base->pppsuffix[0] = '\0'; if (auth->username_suffix != NULL) strlcpy(base->pppsuffix, auth->username_suffix, sizeof(base->pppsuffix)); base->eap_capable = auth->eap_capable; base->strip_nt_domain = auth->strip_nt_domain; base->strip_atmark_realm = auth->strip_atmark_realm; base->has_users_file = 0; base->radius_ready = 0; base->user_max_session = auth->user_max_session; if (strlen(auth->users_file_path) > 0) { strlcpy(base->users_file_path, auth->users_file_path, sizeof(base->users_file_path)); base->has_users_file = 1; } else { if (base->type == NPPPD_AUTH_TYPE_LOCAL) { npppd_auth_base_log(base, LOG_WARNING, "missing users_file property."); goto fail; } } switch (base->type) { #ifdef USE_NPPPD_RADIUS case NPPPD_AUTH_TYPE_RADIUS: if (npppd_auth_radius_reload(base, auth) != 0) goto fail; break; #endif } base->initialized = 1; return 0; fail: base->initialized = 0; base->has_users_file = 0; base->radius_ready = 0; return 1; } /** * This function gets specified user's password. The value 0 is returned * if the call succeeds. * * @param username username which gets the password * @param password buffers which stores the password * Specify NULL if you want to known the length of * the password only. * @param lppassword pointer which indicates the length of * the buffer which stores the password. * @return A value 1 is returned if user is unknown. A value 2 is returned * if password buffer is sufficient. A negative value is * returned if other error occurred. */ int npppd_auth_get_user_password(npppd_auth_base *base, const char *username, char *password, int *plpassword) { int retval, sz, lpassword; npppd_auth_user *user; NPPPD_AUTH_ASSERT(base != NULL); NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username)); user = NULL; retval = 0; if (base->has_users_file == 0) { retval = -1; goto out; } if ((user = npppd_auth_get_user(base, username)) == NULL) { retval = 1; goto out; } if (password == NULL && plpassword == NULL) { retval = 0; goto out; } if (plpassword == NULL) { retval = -1; goto out; } lpassword = strlen(user->password) + 1; sz = *plpassword; *plpassword = lpassword; if (password == NULL) { retval = 0; goto out; } if (sz < lpassword) { retval = 2; goto out; } strlcpy(password, user->password, sz); out: free(user); return retval; } /** * This function gets specified users' Framed-IP-{Address,Netmask}. * The value 0 is returned if the call succeeds. *

* Because authentication database is updated at any time, the password is * possible to be inconsistent if this function is not called immediately * after authentication. So this function is called immediately after * authentication.

* @param username username which gets the password * @param ip4address pointer which indicates struct in_addr which * stores the Framed-IP-Address * @param ip4netmask pointer which indicates struct in_addr which * stores Framed-IP-Netmask */ int npppd_auth_get_framed_ip(npppd_auth_base *base, const char *username, struct in_addr *ip4address, struct in_addr *ip4netmask) { npppd_auth_user *user; NPPPD_AUTH_ASSERT(base != NULL); NPPPD_AUTH_DBG((base, LOG_DEBUG, "%s(%s)", __func__, username)); if (base->has_users_file == 0) return -1; if ((user = npppd_auth_get_user(base, username)) == NULL) return 1; if (user->framed_ip_address.s_addr != 0) { *ip4address = user->framed_ip_address; if (ip4netmask != NULL) *ip4netmask = user->framed_ip_netmask; free(user); return 0; } free(user); return 1; } /** * Retribute "Calling-Number" attribute of the user from the realm. * * @param username Username. * @param number Pointer to the space for the Calling-Number. This * can be NULL in case retributing the Calling-Number only. * @param plnumber Pointer to the length of the space for the * Calling-Number. * @return 0 if the Calling-Number attribute is successfully retributed. * 1 if the user has no Calling-Number attribute. return -1 if the realm * doesn't have user attributes or other errors. return 2 if the space * is not enough. */ int npppd_auth_get_calling_number(npppd_auth_base *base, const char *username, char *number, int *plnumber) { int retval, lcallnum, sz; npppd_auth_user *user; user = NULL; retval = 0; if (base->has_users_file == 0) return -1; if ((user = npppd_auth_get_user(base, username)) == NULL) return 1; if (number == NULL && plnumber == NULL) { retval = 0; goto out; } if (plnumber == NULL) { retval = -1; goto out; } lcallnum = strlen(user->calling_number) + 1; sz = *plnumber; *plnumber = lcallnum; if (sz < lcallnum) { retval = 2; goto out; } strlcpy(number, user->calling_number, sz); out: free(user); return retval; } int npppd_auth_get_type(npppd_auth_base *base) { return base->type; } int npppd_auth_is_usable(npppd_auth_base *base) { return (base->initialized != 0 && base->disposing == 0)? 1 : 0; } int npppd_auth_is_ready(npppd_auth_base *base) { if (!npppd_auth_is_usable(base)) return 0; switch(base->type) { case NPPPD_AUTH_TYPE_LOCAL: return (base->has_users_file)? 1 : 0; /* NOTREACHED */ case NPPPD_AUTH_TYPE_RADIUS: return (base->has_users_file != 0 || base->radius_ready != 0)? 1 : 0; /* NOTREACHED */ } NPPPD_AUTH_ASSERT(0); return 0; } int npppd_auth_is_disposing(npppd_auth_base *base) { return (base->disposing != 0)? 1 : 0; } int npppd_auth_is_eap_capable(npppd_auth_base *base) { return (base->eap_capable != 0)? 1 : 0; } const char * npppd_auth_get_name(npppd_auth_base *base) { return base->name; } const char * npppd_auth_get_suffix(npppd_auth_base *base) { return base->pppsuffix; } const char * npppd_auth_username_for_auth(npppd_auth_base *base, const char *username, char *username_buffer) { const char *u0; char *atmark, *u1; u0 = NULL; if (base->strip_nt_domain != 0) { if ((u0 = strchr(username, '\\')) != NULL) u0++; } if (u0 == NULL) u0 = username; u1 = username_buffer; if (username_buffer != u0) memmove(username_buffer, u0, MINIMUM(strlen(u0) + 1, MAX_USERNAME_LENGTH)); if (base->strip_atmark_realm != 0) { if ((atmark = strrchr(u1, '@')) != NULL) *atmark = '\0'; } return username_buffer; } int npppd_auth_user_session_unlimited(npppd_auth_base *_this) { return (_this->user_max_session == 0) ? 1 : 0; } int npppd_check_auth_user_max_session(npppd_auth_base *_this, int count) { if (!npppd_auth_user_session_unlimited(_this) && _this->user_max_session <= count) return 1; else return 0; } /*********************************************************************** * Account list related functions ***********************************************************************/ static npppd_auth_user * npppd_auth_get_user(npppd_auth_base *base, const char *username) { int lsuffix, lusername; const char *un; char buf[MAX_USERNAME_LENGTH]; npppd_auth_user *u; un = username; lsuffix = strlen(base->pppsuffix); lusername = strlen(username); if (lsuffix > 0 && lusername > lsuffix && strcmp(username + lusername - lsuffix, base->pppsuffix) == 0 && lusername - lsuffix < sizeof(buf)) { memcpy(buf, username, lusername - lsuffix); buf[lusername - lsuffix] = '\0'; un = buf; } if (priv_get_user_info(base->users_file_path, un, &u) == 0) return u; return NULL; } #ifdef USE_NPPPD_RADIUS /*********************************************************************** * RADIUS ***********************************************************************/ /** reload the configuration of RADIUS authentication realm */ static int npppd_auth_radius_reload(npppd_auth_base *base, struct authconf *auth) { npppd_auth_radius *_this = (npppd_auth_radius *)base; radius_req_setting *rad; struct radserver *server; int i, nauth, nacct; _this->rad_auth_setting->timeout = (auth->data.radius.auth.timeout == 0) ? DEFAULT_RADIUS_TIMEOUT : auth->data.radius.auth.timeout; _this->rad_acct_setting->timeout = (auth->data.radius.acct.timeout == 0) ? DEFAULT_RADIUS_TIMEOUT : auth->data.radius.acct.timeout; _this->rad_auth_setting->max_tries = (auth->data.radius.auth.max_tries == 0) ? DEFAULT_RADIUS_MAX_TRIES : auth->data.radius.auth.max_tries; _this->rad_acct_setting->max_tries = (auth->data.radius.acct.max_tries == 0) ? DEFAULT_RADIUS_MAX_TRIES : auth->data.radius.acct.max_tries; _this->rad_auth_setting->max_failovers = (auth->data.radius.auth.max_failovers == 0) ? DEFAULT_RADIUS_MAX_FAILOVERS : auth->data.radius.auth.max_failovers; _this->rad_acct_setting->max_failovers = (auth->data.radius.acct.max_failovers == 0) ? DEFAULT_RADIUS_MAX_FAILOVERS : auth->data.radius.acct.max_failovers; _this->rad_acct_setting->curr_server = _this->rad_auth_setting->curr_server = 0; /* load configs for authentication server */ rad = _this->rad_auth_setting; for (i = 0; i < countof(rad->server); i++) memset(&rad->server[i], 0, sizeof(rad->server[0])); i = 0; TAILQ_FOREACH(server, &auth->data.radius.auth.servers, entry) { if (i >= countof(rad->server)) break; memcpy(&rad->server[i].peer, &server->address, server->address.ss_len); if (((struct sockaddr_in *)&rad->server[i].peer)->sin_port == 0) ((struct sockaddr_in *)&rad->server[i].peer)->sin_port = htons(DEFAULT_RADIUS_AUTH_PORT); strlcpy(rad->server[i].secret, server->secret, sizeof(rad->server[i].secret)); rad->server[i].enabled = 1; i++; } nauth = i; /* load configs for accounting server */ rad = _this->rad_acct_setting; for (i = 0; i < countof(rad->server); i++) memset(&rad->server[i], 0, sizeof(rad->server[0])); i = 0; TAILQ_FOREACH(server, &auth->data.radius.acct.servers, entry) { if (i >= countof(rad->server)) break; memcpy(&rad->server[i].peer, &server->address, server->address.ss_len); if (((struct sockaddr_in *)&rad->server[i].peer)->sin_port == 0) ((struct sockaddr_in *)&rad->server[i].peer)->sin_port = htons(DEFAULT_RADIUS_ACCT_PORT); strlcpy(rad->server[i].secret, server->secret, sizeof(rad->server[i].secret)); rad->server[i].enabled = 1; i++; } nacct = i; for (i = 0; i < countof(_this->rad_auth_setting->server); i++) { if (_this->rad_auth_setting->server[i].enabled) base->radius_ready = 1; } npppd_auth_base_log(&_this->nar_base, LOG_INFO, "Loaded configuration. %d authentication server%s, %d accounting " "server%s.", nauth, (nauth > 1)? "s" : "", nacct, (nacct > 1)? "s" : ""); if (nacct > 0 && _this->rad_acct_on == 0) { radius_acct_on(base->npppd, _this->rad_acct_setting); _this->rad_acct_on = 1; } return 0; } /** * Get {@link ::radius_req_setting} for RADIUS authentication of specified * {@link ::npppd_auth_base} object. */ void * npppd_auth_radius_get_radius_auth_setting(npppd_auth_radius *_this) { return _this->rad_auth_setting; } /** * Get {@link ::radius_req_setting} for RADIUS accounting of specified * {@link ::npppd_auth_base} object. */ void * npppd_auth_radius_get_radius_acct_setting(npppd_auth_radius *_this) { return _this->rad_acct_setting; } #endif /*********************************************************************** * Helper functions ***********************************************************************/ /** Log it which starts the label based on this instance. */ static int npppd_auth_base_log(npppd_auth_base *_this, int prio, const char *fmt, ...) { int status; char logbuf[BUFSIZ]; va_list ap; NPPPD_AUTH_ASSERT(_this != NULL); va_start(ap, fmt); snprintf(logbuf, sizeof(logbuf), "realm name=%s %s", _this->name, fmt); status = vlog_printf(prio, logbuf, ap); va_end(ap); return status; }