RADIUS Management User Authentication Feature (#7284)

Why I did it
HLD: https://github.com/Azure/SONiC/blob/master/doc/aaa/radius_authentication.md
CLI: In a separate PR.

How I did it
How to verify it
UT: src/sonic-host-services/tests/hostcfgd/hostcfgd_radius_test.py
This commit is contained in:
a-barboza 2021-04-23 19:09:41 -07:00 committed by GitHub
parent 990b1127a7
commit ec9101f9c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 11312 additions and 22 deletions

View File

@ -299,6 +299,16 @@ sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/libnss-tacplus_*.deb || \
sudo LANG=C chroot $FILESYSTEM_ROOT pam-auth-update --remove tacplus
sudo sed -i -e '/^passwd/s/ tacplus//' $FILESYSTEM_ROOT/etc/nsswitch.conf
# Install pam-radius-auth and nss-radius
sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/libpam-radius-auth_*.deb || \
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/libnss-radius_*.deb || \
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
# Disable radius by default
# radius does not have any profiles
#sudo LANG=C chroot $FILESYSTEM_ROOT pam-auth-update --remove radius tacplus
sudo sed -i -e '/^passwd/s/ radius//' $FILESYSTEM_ROOT/etc/nsswitch.conf
# Install a custom version of kdump-tools (and its dependencies via 'apt-get -y install -f')
if [[ $CONFIGURED_ARCH == amd64 ]]; then
sudo DEBIAN_FRONTEND=noninteractive dpkg --root=$FILESYSTEM_ROOT -i $debs_path/kdump-tools_*.deb || \

13
rules/radius.dep Normal file
View File

@ -0,0 +1,13 @@
SPATH := $($(LIBPAM_RADIUS)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/radius.mk rules/radius.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
DEP_FILES += $(shell git ls-files $(SPATH))
$(LIBPAM_RADIUS)_CACHE_MODE := GIT_CONTENT_SHA
$(LIBPAM_RADIUS)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(LIBPAM_RADIUS)_DEP_FILES := $(DEP_FILES)
$(LIBNSS_RADIUS)_CACHE_MODE := GIT_CONTENT_SHA
$(LIBNSS_RADIUS)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(LIBNSS_RADIUS)_DEP_FILES := $(DEP_FILES)

24
rules/radius.mk Normal file
View File

@ -0,0 +1,24 @@
# libpam-radius-auth packages
PAM_RADIUS_VERSION = 1.4.1-1
export PAM_RADIUS_VERSION
LIBPAM_RADIUS = libpam-radius-auth_$(PAM_RADIUS_VERSION)_amd64.deb
$(LIBPAM_RADIUS)_SRC_PATH = $(SRC_PATH)/radius/pam
SONIC_MAKE_DEBS += $(LIBPAM_RADIUS)
SONIC_STRETCH_DEBS += $(LIBPAM_RADIUS)
# libnss-radius packages
NSS_RADIUS_VERSION = 1.0.1-1
export NSS_RADIUS_VERSION
LIBNSS_RADIUS = libnss-radius_$(NSS_RADIUS_VERSION)_amd64.deb
$(LIBNSS_RADIUS)_SRC_PATH = $(SRC_PATH)/radius/nss
SONIC_MAKE_DEBS += $(LIBNSS_RADIUS)
SONIC_STRETCH_DEBS += $(LIBNSS_RADIUS)

View File

@ -858,6 +858,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
$(IFUPDOWN2) \
$(KDUMP_TOOLS) \
$(NTP) \
$(LIBPAM_RADIUS) \
$(LIBNSS_RADIUS) \
$(LIBPAM_TACPLUS) \
$(LIBNSS_TACPLUS) \
$(MONIT) \

23
src/radius/nss/Makefile Normal file
View File

@ -0,0 +1,23 @@
.ONESHELL:
SHELL = /bin/bash
.SHELLFLAGS += -e
MAIN_TARGET = libnss-radius_$(NSS_RADIUS_VERSION)_amd64.deb
$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
pushd ./libnss-radius
make clean
-rm -rf debian
-rm -rf patches
cp -r ../debian .
cp -r ../patches .
# Apply patch (if any)
dpkg-buildpackage -rfakeroot -b -us -uc
popd
mv $(DERIVED_TARGETS) $* $(DEST)/
$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)

View File

@ -0,0 +1,5 @@
libnss-radius for Debian
The Debian version of the libnss-radius package.
-- Arun Barboza <29963827+a-barboza@users.noreply.github.com> Tue, 24 Sep 2019 00:20:55 +0000

View File

@ -0,0 +1,6 @@
libnss-radius (1.0.1-1) unstable; urgency=low
* Initial release. NSS lookups for RADIUS users with cached Management
Privilege Level (MPL) attribute.
-- Arun Barboza <29963827+a-barboza@users.noreply.github.com> Tue, 24 Sep 2019 00:20:55 +0000

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,17 @@
Source: libnss-radius
Section: libs
Priority: optional
Maintainer: Arun Barboza <29963827+a-barboza@users.noreply.github.com>
Build-Depends: debhelper (>=9)
Standards-Version: 3.9.6
Homepage: http://www.broadcom.com
Package: libnss-radius
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: NSS module for RADIUS authentication absent local account.
NSS lookups for RADIUS authenticated users using the Management Privilege
Level (MPL) cached attribute.

View File

@ -0,0 +1,21 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: libnss-radius
Source: http://www.broadcom.com
Files: *
Copyright: 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
License: Apache
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

15
src/radius/nss/debian/rules Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/make -f
# You must remove unused comment lines for the released package.
#export DH_VERBOSE = 1
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $@
#override_dh_auto_install:
# dh_auto_install -- prefix=/usr
#override_dh_install:
# dh_install --list-missing -X.pyc -X.pyo

View File

@ -0,0 +1,2 @@
# You must remove unused comment lines for the released package.
version=3

View File

@ -0,0 +1,6 @@
cache_radius
libnss_radius.so.2
test_cache_radius
test_nss_radius
debian
patches

View File

@ -0,0 +1,14 @@
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,59 @@
#######################################################################
#
# Copyright 2019 Broadcom. All rights reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
#
#######################################################################
#
# Makefile for libnss-radius
#
TARGETS = libnss_radius.so.2 cache_radius
COMMON_INCLUDE = nss_radius_common.h
COMMON_SOURCE = nss_radius_common.c
LIBNSS_SOURCE = nss_radius.c $(COMMON_SOURCE)
CACHE_SOURCE = cache_radius.c $(COMMON_SOURCE)
# For now place the multiarch flag here
# Eventually this needs to be move to the debian packaging
#moduledir = $(prefix)/lib/x86_64-linux-gnu
moduledir = $(prefix)/lib/$(DEB_HOST_MULTIARCH)
all: $(TARGETS)
libnss_radius.so.2: $(LIBNSS_SOURCE) $(COMMON_INCLUDE)
$(CC) $(CFLAGS) $(LDFLAGS) -fPIC -Wall -shared -o libnss_radius.so.2 \
-Wl,-soname,libnss_radius.so.2 -Wl,--version-script=libnss_radius_vs.txt $(LIBNSS_SOURCE)
cache_radius: $(CACHE_SOURCE) $(COMMON_INCLUDE)
$(CC) $(CFLAGS) $(LDFLAGS) -o cache_radius $(CACHE_SOURCE)
clean:
-rm -f $(TARGETS)
-rm -f test_nss_radius test_cache_radius
install: libnss_radius.so.2 cache_radius
install -m 0644 -D libnss_radius.so.2 \
$(DESTDIR)$(moduledir)/libnss_radius.so.2
install -m 0755 -D cache_radius \
$(DESTDIR)$(prefix)/usr/sbin/cache_radius
install -m 0755 -d $(DESTDIR)$(prefix)/etc/pam_radius_auth.d/
distclean: clean
uninstall:
-rm -f $(DESTDIR)$(moduledir)/libnss_radius.so.2
-rm -f $(DESTDIR)$(prefix)/usr/sbin/cache_radius
test: test_nss_radius.c $(LIBNSS_SOURCE) $(CACHE_SOURCE) \
$(COMMON_SOURCE) $(COMMON_INCLUDE)
$(CC) $(CFLAGS) $(LDFLAGS) -g -DTEST_RADIUS_NSS -o test_nss_radius \
$(LIBNSS_SOURCE) test_nss_radius.c
$(CC) $(CFLAGS) $(LDFLAGS) -g -DTEST_RADIUS_NSS -o test_cache_radius \
$(CACHE_SOURCE)
.PHONY: all install clean distclean uninstall test

View File

@ -0,0 +1,198 @@
/*
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* cache_radius to be invoked after a successful pam_radius_auth.so
* module invocation as:
*
* auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
*
* cache_radius -u to be invoked to clear aged unconfirmed users.
*
* auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius -u
*
*/
#include "nss_radius_common.h"
static int radius_update_cache_cleanup(int status, mode_t mask, FILE * fp) {
/* Umask restore.
*/
umask(mask);
if (fp)
fclose(fp);
return status;
}
static int radius_update_cache( char * prog, const char * nam, int mpl) {
mode_t mask;
char filename[PATH_MAX];
int written;
struct stat sb;
FILE * fp = NULL;
/* Umask save, change.
*/
mask = umask(022);
if (((written = snprintf( filename, sizeof(filename), "%s/%s",
RADIUS_ATTRIBUTE_CACHE_DIR, nam)) + sizeof(RADIUS_ATTR_MPL) + 1)
>= sizeof(filename)) {
syslog( LOG_ERR, "%s: \"%s\": too long.", prog, filename);
return(radius_update_cache_cleanup(STATUS_EINVAL, mask, fp));
}
/* Create the directory.
*/
if ((!((stat( RADIUS_CACHE_DIR, &sb) == 0) && S_ISDIR(sb.st_mode))
&& (mkdir( RADIUS_CACHE_DIR, 0755) == -1))
|| (!((stat( RADIUS_ATTRIBUTE_CACHE_DIR, &sb) == 0) && S_ISDIR(sb.st_mode))
&& (mkdir( RADIUS_ATTRIBUTE_CACHE_DIR, 0755) == -1))
|| (!((stat( filename, &sb) == 0) && S_ISDIR(sb.st_mode))
&& (mkdir( filename, 0755) == -1))) {
syslog( LOG_ERR, "%s: \"%s\": mkdir() fails. Or ! dir errno %d",
prog, filename, errno);
return(radius_update_cache_cleanup(STATUS_ENOENT, mask, fp));
}
/* Create the file.
*/
strcat( filename, "/");
strcat( filename, RADIUS_ATTR_MPL);
if ( ((fp = fopen( filename, "w")) == NULL)
|| (fprintf(fp, "%d\n", mpl) < 0)) {
syslog( LOG_ERR,
"%s: \"%s\": fopen(): %p or fprintf() fails. errno %d", prog,
filename, (void *) fp, errno);
return(radius_update_cache_cleanup(STATUS_EPERM, mask, fp));
}
syslog(LOG_INFO, "%s: MPL %d updated for user %s", prog, mpl, nam);
return(radius_update_cache_cleanup(0, mask, fp));
}
static int main_cleanup(int status, RADIUS_NSS_CONF_B * conf, int * pncfd) {
int my_errno = 0;
if (conf)
unparse_nss_config(conf, &my_errno, pncfd);
return status;
}
int main(int ac, char * av[]) {
int mpl = 1, cached_mpl = 1;
int status = 0;
int my_errno = 0;
char * user = NULL, * privilege;
RADIUS_NSS_CONF_B radius_nss_conf, * conf = & radius_nss_conf;
RADIUS_NSS_MPL * rnm;
char file_buf[RADIUS_MAX_NSS_CONF_SZ];
int ncfd = -1;
int no_clear_unconfirmed = 0;
int clear_unconfirmed_limit = 0;
int refresh_user = 0;
char buf[BUFLEN];
struct passwd pw, *result = NULL;
/* Set the logging parameters.
*/
openlog(av[0], LOG_CONS | LOG_PID, LOG_AUTHPRIV);
/* Parse Command-Line options.
*/
if ((ac == 2) && (strncmp(av[1], "-n", 3) == 0)) {
no_clear_unconfirmed = 1;
} else if (ac > 1) {
syslog(LOG_WARNING,
"%s: Ignoring unknown option:\"%s\"\n", av[0], av[1]);
}
/* Read environment PAM_USER.
*/
if ( ((user = getenv("PAM_USER")) == NULL)
|| (user[0] == 0)) {
syslog(LOG_WARNING,
"%s: Missing or bad PAM_USER in environment:\"%s\"\n",
av[0], user ? user : "(null)");
exit(main_cleanup(STATUS_ENOENT, NULL, NULL));
}
/* Read environment Privilege.
*/
if ((privilege = getenv("Privilege")) != NULL) {
mpl = atoi(privilege);
if (!((RADIUS_MIN_MPL <= mpl) && (mpl <= RADIUS_MAX_MPL))) {
syslog(LOG_WARNING, "%s: Invalid Management-Privilege-Level %d\n",
av[0], mpl);
mpl = 1;
}
} else {
syslog(LOG_INFO, "%s: Missing or bad Privilege in environment:\"%s\"\n",
av[0], privilege ? privilege : "");
}
parse_nss_config(conf, av[0], file_buf, sizeof(file_buf), &my_errno, &ncfd);
if ( (radius_lookup_cache( conf->prog, user, &cached_mpl) != 0)
|| (cached_mpl != mpl)
|| (radius_getpwnam_r(conf->prog, user, &pw, buf, sizeof(buf),&result)
!= 0)) {
radius_update_cache( conf->prog, user, mpl);
refresh_user = 1;
}
if (conf->many_to_one) {
rnm = &((conf->rnm)[mpl-1]);
if (radius_getpwnam_r(conf->prog, rnm->gecos, &pw, buf, sizeof(buf),
&result) != 0) {
radius_create_user(conf, rnm->gecos, mpl, RADIUS_CONFIRMED);
}
if (mpl != cached_mpl) {
syslog(LOG_WARNING, "%s: Management-Privilege-Level init/changed:"
" %d --> %d\n", conf->prog, cached_mpl, mpl);
fprintf(stdout, "Management-Privilege-Level inited or changed.\n");
fprintf(stdout, "Please login again.\n");
exit(main_cleanup(STATUS_EPERM, conf, &ncfd));
}
} else if (!(conf->many_to_one) && refresh_user) {
radius_update_user( conf, user, mpl);
}
if (!no_clear_unconfirmed && (conf->many_to_one == 0)) {
clear_unconfirmed_limit = conf->unconfirmed_clear_limit;
while(radius_clear_unconfirmed_users(conf) == 0) {
if (clear_unconfirmed_limit-- <= 0) {
syslog(LOG_INFO, "%s: Clear unconfirmed limit %d reached:",
conf->prog, conf->unconfirmed_clear_limit);
break;
}
}
}
exit(main_cleanup(0, conf, &ncfd));
}

View File

@ -0,0 +1,4 @@
{
global: _nss_radius_getpwnam_r;
local: *;
};

View File

@ -0,0 +1,84 @@
/*
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* plugin implements getpwnam_r for NSS for RADIUS cached MPL(Management
* Privilege Attribute).
*/
#include "nss_radius_common.h"
/*
* NSS entry point for getpwnam().
*/
enum nss_status _nss_radius_getpwnam_r( const char * nam, struct passwd * pwd,
char * buf, size_t buflen, int * errnop) {
enum nss_status status = NSS_STATUS_NOTFOUND;
int mpl = 1;
int ncfd = -1;
RADIUS_NSS_CONF_B radius_nss_conf, * conf = &(radius_nss_conf);
RADIUS_NSS_MPL * rnm;
char file_buf[RADIUS_MAX_NSS_CONF_SZ];
char buffer[BUFLEN];
struct passwd pw, *res = NULL;
char * prog = "nss";
/* Ignore filename completion.
*/
if (!nam || !strcmp(nam, "*") || !pwd || !buf || (buflen == 0))
return NSS_STATUS_NOTFOUND;
parse_nss_config(conf, prog, file_buf, sizeof(file_buf), errnop, &ncfd);
if (radius_lookup_cache(prog, nam, &mpl) == 0) {
/* The MPL exists for this user in the cache.
*/
if (conf->debug)
syslog( LOG_DEBUG, "%s: nam: %s", prog, nam);
rnm = &((conf->rnm)[mpl-1]);
if (conf->many_to_one) {
radius_getpwnam_r(prog, rnm->gecos, &pw, buffer, sizeof(buffer),
&res);
} else if (conf->allow_anonymous) {
radius_create_user(conf, nam, mpl, RADIUS_CONFIRMED);
radius_getpwnam_r(prog, nam, &pw, buffer, sizeof(buffer), &res);
}
} else if (conf->allow_anonymous && is_sshd_lookup(conf, nam)) {
/* Could be an sshd doing a getpwnam() before pam_authenticate().
*/
rnm = &((conf->rnm)[mpl-1]);
if (conf->many_to_one) {
if (radius_getpwnam_r(prog, rnm->gecos, &pw, buffer, sizeof(buffer),
&res) != 0) {
radius_create_user(conf, rnm->gecos, mpl, RADIUS_CONFIRMED);
radius_getpwnam_r(prog, rnm->gecos, &pw, buffer, sizeof(buffer),
&res);
}
} else {
radius_create_user(conf, nam, mpl, RADIUS_UNCONFIRMED);
radius_getpwnam_r(prog, nam, &pw, buffer, sizeof(buffer), &res);
}
}
/* Fill the pwd
*/
if (res) {
status = NSS_STATUS_SUCCESS;
radius_copy_pw(conf, res, nam, pwd, buf, buflen, errnop);
}
unparse_nss_config(conf, errnop, &ncfd);
return status;
}

View File

@ -0,0 +1,780 @@
/*
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* Common code for NSS for RADIUS cached MPL(Management Privilege Attribute).
* Used by both NSS module(uses the cache) and PAM module(updates the cache)
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <nss.h>
#include <sys/file.h>
#include <regex.h>
#include <time.h>
#include "nss_radius_common.h"
static void dump_rnm(int mpl, RADIUS_NSS_MPL * rnm, char * msg) {
syslog( LOG_DEBUG, "dump_rnm: %s:"
"mpl %d gid %d groups \"%s\" gecos \"%s\" shell \"%s\"",
(msg ? msg : ""), mpl, rnm->gid, rnm->groups, rnm->gecos, rnm->shell);
}
static char * parse_line(RADIUS_NSS_CONF_B * conf, char * file_buf,
char ** pscanpos, int * premain) {
char * line = NULL;
int skipline = 0;
for( ; (*premain); (*pscanpos)++, (*premain)--) {
if (skipline) {
if ( ((**pscanpos) == '\n')
|| ((**pscanpos) == '\r')
|| ((**pscanpos) == '\f')) {
skipline = 0;
}
continue;
} else if (line == NULL) {
if (isspace(**pscanpos)) {
continue;
} else if ((**pscanpos) == '#') {
skipline = 1;
} else {
line = *pscanpos;
}
continue;
} else if ( ((**pscanpos) == '\n')
|| ((**pscanpos) == '\r')
|| ((**pscanpos) == '\f')) {
break;
}
}
if (line) {
*((*pscanpos)++) = 0;
if (*premain)
(*premain)--;
}
if (conf->trace)
syslog(LOG_DEBUG, "parse_line: \"%s\"\n", line ? line : "");
return line;
}
static char * parse_token(RADIUS_NSS_CONF_B * conf, char ** pbufpos,
int * premain) {
char * token = NULL;
for( token = *pbufpos;
(*premain) && (**pbufpos) && ((**pbufpos) != ';') ;
(*pbufpos)++, (*premain)--)
; /* Empty Body */
if (*premain) {
*((*pbufpos)++) = 0;
(*premain)--;
}
if (conf->trace)
syslog(LOG_DEBUG, "parse_token: \"%s\"\n", token ? token : "");
return token;
}
static int parse_user(RADIUS_NSS_CONF_B * conf, char * line) {
RADIUS_NSS_MPL parse_rnm;
char * token, * bufpos;
int mpl = 0;
int ret = 0, remain;
bufpos = line;
remain = strlen(line);
memset((char *) &parse_rnm, 0, sizeof(parse_rnm));
while (remain && (token = parse_token(conf, &bufpos, &remain))) {
if (strncmp(token, "user_priv=", 10) == 0) {
mpl = atoi(&token[10]);
} else if (strncmp(token, "group=", 6) == 0) {
parse_rnm.groups = &(token[6]);
} else if (strncmp(token, "pw_info=", 8) == 0) {
parse_rnm.gecos = &(token[8]);
} else if (strncmp(token, "uid=", 4) == 0) {
syslog( LOG_WARNING, "%s: Ignoring \"%s\"."
" Using system assigned User ID", RADIUS_NSS_CONF, token);
} else if (strncmp(token, "gid=", 4) == 0) {
parse_rnm.gid = atol(&(token[4]));
} else if (strncmp(token, "dir=", 4) == 0) {
syslog( LOG_WARNING, "%s: Ignoring \"%s\"."
" Using system assigned home directory",
RADIUS_NSS_CONF, token);
} else if (strncmp(token, "shell=", 6) == 0) {
parse_rnm.shell = &(token[6]);
}
}
if (conf->trace)
dump_rnm(mpl, &parse_rnm, "parse");
if ( ((RADIUS_MIN_MPL > mpl) || (mpl > RADIUS_MAX_MPL))
|| (parse_rnm.gid == 0)
|| (parse_rnm.groups == NULL)
|| (parse_rnm.gecos == NULL)
|| (parse_rnm.shell == NULL)) {
syslog( LOG_WARNING, "%s: Bad user_priv line \"%s\"", RADIUS_NSS_CONF,
line);
ret = 1;
} else {
(conf->rnm)[mpl-1] = parse_rnm;
}
return ret;
}
static void init_rnm(RADIUS_NSS_CONF_B * conf) {
RADIUS_NSS_MPL * rnm = conf->rnm;
/* Set rnm[0,max_mpl-1].
*/
memset((char *) rnm, 0, sizeof(conf->rnm));
rnm[0].gid = 999;
rnm[0].groups = "docker";
rnm[0].gecos = "remote_user";
rnm[0].shell = "/usr/bin/sonic-launch-shell";
rnm[RADIUS_MAX_MPL-1].gid = 1000;
rnm[RADIUS_MAX_MPL-1].groups = "admin,sudo,docker";
rnm[RADIUS_MAX_MPL-1].gecos = "remote_user_su";
rnm[RADIUS_MAX_MPL-1].shell = "/usr/bin/sonic-launch-shell";
}
int parse_nss_config(RADIUS_NSS_CONF_B * conf, char * prog,
char * file_buf, int file_buf_sz, int * errnop, int * plockfd) {
/* Slurp the whole file.
*/
int ncfd = -1;
int ret = 0;
int i = 0, remain;
struct stat sb;
char errbuf[128];
int flags;
char * scanpos, * line;
int use_default_rnm = 1, bad_rnm = 0;
int unconfirmed_disallow = 0;
memset((char *)conf, 0, sizeof(*conf));
conf->prog = prog;
conf->allow_anonymous = 1;
conf->unconfirmed_ageout = UNCONFIRMED_AGEOUT_DEFAULT;
conf->unconfirmed_clear_limit = UNCONFIRMED_CLEAR_LIMIT_DEFAULT;
/* Read the file.
*/
if (((ncfd = open(RADIUS_NSS_CONF, O_RDONLY)) == -1)
|| (fstat(ncfd, &sb) == -1)
|| (((flags = fcntl(ncfd, F_GETFL, 0)) == -1) && ((flags = 0) != 0))
|| (fcntl(ncfd, F_SETFL, flags | O_NONBLOCK) == -1)) {
if (errnop)
*errnop = errno;
ret = 1;
errbuf[0] = 0; strerror_r(errno, errbuf, sizeof(errbuf));
syslog( LOG_WARNING, "%s: %s", prog, errbuf);
goto parse_nss_config_exit;
}
/* The maximum file size is 1 less than the buffer, to allow space for
* a NULL byte in the case where the last line has no \n, \r, \l char.
* (which could have been substituted with a NULL).
*/
if (sb.st_size >= file_buf_sz) {
syslog( LOG_WARNING, "%s: size greater than %d. Ignoring",
prog, file_buf_sz - 1);
goto parse_nss_config_exit;
}
if ((i = read(ncfd, file_buf, file_buf_sz)) != sb.st_size) {
syslog( LOG_WARNING, "%s: read %d of %ld. Ignoring", prog,
i, sb.st_size);
goto parse_nss_config_exit;
}
/* Parse each line from file.
*/
scanpos = file_buf;
remain = sb.st_size;
while((line = parse_line(conf, file_buf, &scanpos, &remain)) != NULL) {
if (strncmp(line, "debug=", 6) == 0) {
if (strncmp(&(line[6]), "on", 2) == 0) {
/* Handle "debug".
*/
conf->debug = 1;
} else if (strncmp(&(line[6]), "trace", 5) == 0) {
conf->debug = 1;
conf->trace = 1;
}
} else if (strncmp(line, "user_priv=", 10) == 0) {
/* Handle "user_priv"
*/
use_default_rnm = 0;
if (parse_user(conf, line)) {
bad_rnm = 1;
}
} else if (strncmp(line, "many_to_one=", 12) == 0) {
/* Handle "many_to_one"
*/
if ((strncmp(&(line[12]), "y", 2) == 0) ||
(strncmp(&(line[12]), "ye", 3) == 0) ||
(strncmp(&(line[12]), "yes", 4) == 0)) {
conf->many_to_one = 1;
} else if (strncmp(&(line[12]), "a", 1) == 0) {
conf->many_to_one = 0;
syslog( LOG_WARNING, "%s: a(nonymous) is same as n(o) option:"
" \"%s\"", prog, line);
} else if ((strncmp(&(line[12]), "n", 2) == 0) ||
(strncmp(&(line[12]), "no", 3) == 0)) {
conf->many_to_one = 0;
} else {
syslog( LOG_WARNING, "%s: Ignorning \"%s\"", prog, line);
}
} else if (strncmp(line, "unconfirmed_regexp=", 19) == 0) {
conf->unconfirmed_regexp = &(line[19]);
} else if (strncmp(line, "unconfirmed_ageout=", 19) == 0) {
conf->unconfirmed_ageout = atoi(&(line[19]));
} else if (strncmp(line, "unconfirmed_disallow=", 21) == 0) {
if ((strncmp(&(line[21]), "y", 2) == 0) ||
(strncmp(&(line[21]), "ye", 3) == 0) ||
(strncmp(&(line[21]), "yes", 4) == 0)) {
unconfirmed_disallow = 1;
} else if ((strncmp(&(line[21]), "n", 2) == 0) ||
(strncmp(&(line[21]), "no", 3) == 0)) {
unconfirmed_disallow = 0;
} else {
syslog( LOG_WARNING, "%s: Ignorning \"%s\"", prog, line);
}
} else {
syslog( LOG_WARNING, "%s: Ignoring \"%s\"", prog, line);
}
}
if (unconfirmed_disallow) {
conf->allow_anonymous = 0;
}
parse_nss_config_exit:
if (ncfd != -1) {
if (flock(ncfd, LOCK_EX|LOCK_NB) == 0) {
if (conf->debug)
syslog( LOG_DEBUG, "%s: %d: Unconfirmed: lock success",
prog, (int) getpid());
} else {
conf->allow_anonymous = 0;
if (conf->debug)
syslog( LOG_DEBUG, "%s: %d: Unconfirmed: locked out",
prog, (int) getpid());
}
if (plockfd) {
*plockfd = ncfd;
} else {
close(ncfd); /* This should release the lock, if we placed one */
}
}
/* Fix up rnm.
*/
if (use_default_rnm || bad_rnm)
init_rnm(conf);
for ( i = 1; i < RADIUS_MAX_MPL; i++) {
if ((conf->rnm)[i].gecos == NULL) {
(conf->rnm)[i] = (conf->rnm)[i-1];
}
}
return ret;
}
/* Releases any memory.
* Closes any fds.
*/
int unparse_nss_config(RADIUS_NSS_CONF_B * conf, int * errnop, int * plockfd) {
/* If the caller had a lock in parse_nss_config(),
* we should now release that lock.
*/
if (plockfd && (*plockfd != -1)) {
if (flock(*plockfd, LOCK_UN|LOCK_NB) == 0) {
if (conf->debug)
syslog( LOG_DEBUG, "%s: %d: Unconfirmed: unlock success",
conf->prog, (int) getpid());
} else {
syslog( LOG_ERR, "%s: %d: Unconfirme: unlock fail",
conf->prog, (int) getpid());
}
close(*plockfd);
*plockfd = -1;
}
return 0;
}
static int invoke_popen(RADIUS_NSS_CONF_B * conf, char * cmd) {
FILE * fp;
int status = 0;
if (conf->debug)
syslog(LOG_DEBUG, "%s:%s", conf->prog, cmd);
if (((fp = popen(cmd, "r")) == NULL) || (pclose(fp) == -1)) {
syslog(LOG_ERR, "%s: %s: popen()/pclose() failed %p, errno=%d",
conf->prog, cmd, fp, errno);
status = errno;
}
return status;
}
static int radius_getpwnam_r_cleanup(int status, FILE * fp) {
if (fp)
fclose(fp);
return status;
}
int radius_getpwnam_r(char * prog, const char * name,
struct passwd * pwd, char * buf, int buflen, struct passwd ** result) {
FILE *fp;
int status;
if ((fp = fopen(ETC_PASSWD, "r")) == NULL) {
syslog(LOG_ERR, "%s: fopen(\"/etc/passwd\") failed\n", prog);
return radius_getpwnam_r_cleanup(STATUS_ENOENT, fp);
}
while((status = fgetpwent_r(fp, pwd, buf, buflen, result)) == 0) {
if ( (*result)
&& (*result)->pw_name
&& (strcmp((*result)->pw_name, name) == 0)) {
return radius_getpwnam_r_cleanup(status, fp);
}
}
syslog(LOG_INFO, "%s: User %s not found in local db: %d\n", prog, name,
status);
return radius_getpwnam_r_cleanup(status, fp);
}
static int radius_update_user_cleanup(int status) {
return status;
}
int radius_update_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl) {
char buf[BUFLEN];
char usermod[4096];
struct passwd pw, *result = NULL;
RADIUS_NSS_MPL * rnm = NULL;
int written = 0;
int status;
/* Verify uid is not in the reserved range (<=1000).
*/
if ((status = radius_getpwnam_r(conf->prog, user, &pw, buf, sizeof(buf),
&result)) != 0) {
/* /bin/login does NSS after PAM.
*/
status = radius_create_user(conf, user, mpl, RADIUS_CONFIRMED);
return radius_update_user_cleanup(status);
}
if (result->pw_uid <= 1000) {
syslog(LOG_INFO, "%s: Not changing \"%s\", uid(%d) <= 1000",
conf->prog, user, result->pw_uid);
return radius_update_user_cleanup(STATUS_EPERM);
}
syslog(LOG_INFO, "%s: Adjusting Supplementary Groups for \"%s\"",
conf->prog, user);
rnm = &((conf->rnm)[mpl-1]);
if (conf->trace)
dump_rnm(mpl, rnm, "update");
written = snprintf(usermod, sizeof(usermod),
"%s -G %s -c \"%s\" \"%s\"", USERMOD, rnm->groups, user, user);
if (written >= sizeof(usermod)) {
syslog(LOG_ERR,
"%s: truncated usermod cmd. Skipping:\"%s\"\n", conf->prog, usermod);
return radius_update_user_cleanup(STATUS_E2BIG);
}
return radius_update_user_cleanup(invoke_popen(conf, usermod));
}
static int radius_create_user_cleanup(int status) {
return status;
}
int radius_create_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl,
int unconfirmed) {
char buf[BUFLEN];
char useradd[4096];
RADIUS_NSS_MPL * rnm = &((conf->rnm)[mpl-1]);
int written = 0;
if (conf->trace)
dump_rnm(mpl, rnm, "create");
if (conf->many_to_one) {
written = snprintf(useradd, sizeof(useradd),
"%s -g %d -G %s -c \"%s\" -m -s %s \"%s\"",
USERADD, rnm->gid, rnm->groups, rnm->gecos, rnm->shell, user);
} else {
snprintf(buf, sizeof(buf), "Unconfirmed-%ld", time(NULL));
written = snprintf(useradd, sizeof(useradd),
"%s -U -G %s -c \"%s\" -d \"/home/%s\" -m -s %s \"%s\"",
USERADD, rnm->groups, unconfirmed ? buf : user, user,
rnm->shell, user);
}
if (written >= sizeof(useradd)) {
syslog(LOG_ERR,
"%s: truncated useradd cmd. Skipping:\"%s\"\n", conf->prog, useradd);
return radius_create_user_cleanup(STATUS_E2BIG);
}
syslog(LOG_INFO, "%s: Creating user \"%s\"", conf->prog, user);
return radius_create_user_cleanup(invoke_popen(conf, useradd));
}
static int radius_delete_user_cleanup(int status) {
return status;
}
int radius_delete_user(RADIUS_NSS_CONF_B * conf, const char * user) {
char buf[BUFLEN];
char userdel[4096];
int written = 0;
written = snprintf(userdel, sizeof(userdel), "%s -r \"%s\"", USERDEL, user);
if (written >= sizeof(userdel)) {
syslog(LOG_ERR,
"%s: truncated userdel cmd. Skipping:\"%s\"\n", conf->prog, userdel);
return radius_delete_user_cleanup(STATUS_E2BIG);
}
syslog(LOG_INFO, "%s: Deleting user \"%s\"", conf->prog, user);
return radius_delete_user_cleanup(invoke_popen(conf, userdel));
}
int radius_clear_unconfirmed_users_cleanup(int status, FILE * fp) {
if (fp)
fclose(fp);
return status;
}
int radius_clear_unconfirmed_users(RADIUS_NSS_CONF_B * conf)
{
FILE *fp;
int status;
time_t ts, curr = time(NULL);
struct passwd pw, * pwd = & pw, * result = NULL;
char buf[BUFLEN];
if ((fp = fopen(ETC_PASSWD, "r")) == NULL) {
syslog(LOG_ERR, "%s: fopen(\"/etc/passwd\") failed\n", conf->prog);
return radius_clear_unconfirmed_users_cleanup(STATUS_ENOENT, fp);
}
while((status = fgetpwent_r(fp, pwd, buf, sizeof(buf), &result)) == 0) {
if ( (result)
&& (strncmp((result)->pw_gecos, "Unconfirmed-", 12) == 0)
&& (ts = atoi(&(((result)->pw_gecos)[12])))
&& ((curr - ts) >= conf->unconfirmed_ageout)) {
syslog(LOG_INFO, "%s: Deleting unconfirmed user \"%s\"",
conf->prog, (result)->pw_name);
return radius_clear_unconfirmed_users_cleanup(
radius_delete_user(conf, (result)->pw_name), fp);
}
}
return radius_clear_unconfirmed_users_cleanup(STATUS_ESRCH, fp);
}
int radius_lookup_cache_cleanup( int status, int rafd) {
if (rafd != -1)
close(rafd);
return status;
}
int radius_lookup_cache( char * prog, const char * nam, int * pmpl) {
int rafd = -1;
int i;
char cache_filename[PATH_MAX];
char file_buf[10];
struct stat sb;
int flags;
int written;
int mpl;
*pmpl = RADIUS_MIN_MPL;
if ((written = snprintf(cache_filename, sizeof(cache_filename), "%s/%s/%s",
RADIUS_ATTRIBUTE_CACHE_DIR, nam, RADIUS_ATTR_MPL)
>= sizeof(cache_filename))) {
syslog( LOG_ERR, "%s: \"%s\": too long.", prog, cache_filename);
return radius_lookup_cache_cleanup(STATUS_E2BIG, rafd);
}
/* Ensure the user's cache exists
*/
if (((rafd = open(cache_filename, O_RDONLY)) == -1)
|| (fstat(rafd, &sb) == -1)
|| (((flags = fcntl(rafd, F_GETFL, 0)) == -1) && ((flags = 0) != 0))
|| (fcntl(rafd, F_SETFL, flags | O_NONBLOCK) == -1)) {
syslog( LOG_INFO, "%s: \"%s\": Absent.", prog, cache_filename);
return radius_lookup_cache_cleanup(STATUS_ENOENT, rafd);
}
/* Read the privilege attribute file.
*/
if (sb.st_size >= sizeof(file_buf)) {
syslog( LOG_WARNING, "%s: size %ld greater than %ld. Ignoring",
cache_filename, sb.st_size, sizeof(file_buf)-1);
return radius_lookup_cache_cleanup(STATUS_EFBIG, rafd);
}
if ((i = read(rafd, file_buf, sizeof(file_buf))) != sb.st_size) {
syslog( LOG_WARNING, "%s: read %d of %ld. Ignoring", prog,
i, sb.st_size);
return radius_lookup_cache_cleanup(STATUS_EIO, rafd);
}
file_buf[sb.st_size] = 0;
mpl = atoi(file_buf);
if (((RADIUS_MIN_MPL <= mpl) && (mpl <= RADIUS_MAX_MPL)))
*pmpl = mpl;
return radius_lookup_cache_cleanup(0, rafd);
}
int radius_copy_pw( RADIUS_NSS_CONF_B * conf, struct passwd * res,
const char * nam, struct passwd * pwd,
char * buffer, size_t buflen, int * errnop) {
int ret = 0;
size_t namlen, gecoslen, dirlen, shelllen;
size_t totallen = (namlen = strlen(res->pw_name)) + 1
+ (gecoslen = strlen(res->pw_gecos)) + 1
+ (dirlen = strlen(res->pw_dir)) + 1
+ (shelllen = strlen(res->pw_shell)) + 1
+ 1 + 1;
if (totallen > buflen) {
if (errnop)
*errnop = ERANGE;
ret = 1;
if (conf->debug)
syslog(LOG_INFO, "(%s->%s): %s: getpwnam_r ERANGE %ld < %ld\n",
conf->prog, nam, res->pw_name, buflen, totallen);
return ret;
}
memcpy( buffer, res->pw_name, namlen + 1);
pwd->pw_name = buffer;
buffer += (namlen + 1);
memcpy( buffer, "*", 1 + 1);
pwd->pw_passwd = buffer;
buffer += (1 + 1);
pwd->pw_uid = res->pw_uid;
pwd->pw_gid = res->pw_gid;
memcpy( buffer, res->pw_gecos, gecoslen + 1);
pwd->pw_gecos = buffer;
buffer += (gecoslen + 1);
memcpy( buffer, res->pw_dir, dirlen + 1);
pwd->pw_dir = buffer;
buffer += (dirlen + 1);
memcpy( buffer, res->pw_shell, shelllen + 1);
pwd->pw_shell = buffer;
buffer += (shelllen + 1);
return ret;
}
static int is_sshd_lookup_exit(int status, int fd, regex_t * re) {
if (fd != -1)
close(fd);
if (re)
regfree(re);
return status;
}
int is_sshd_lookup(RADIUS_NSS_CONF_B * conf, const char * name) {
pid_t pid = getpid();
int fd, i;
regex_t regex, * re = NULL;
int reg_ret;
#define PROC_FILENAME_LEN 128
#define CMDLINE_SZ 128
char proc_file[PROC_FILENAME_LEN];
char cmdline[CMDLINE_SZ], * colon;
char expected[CMDLINE_SZ];
char accepted[CMDLINE_SZ];
char errbuf[CMDLINE_SZ];
snprintf(proc_file, sizeof(proc_file), "/proc/%ld/cmdline", (long) pid);
snprintf(expected, sizeof(expected), ": %s [priv]", name);
snprintf(accepted, sizeof(accepted), ": [accepted]");
if ((fd = open(proc_file, O_RDONLY)) == -1) {
syslog( LOG_WARNING, "%s: open(%s) failed: errno %d",
conf->prog, proc_file, errno);
return is_sshd_lookup_exit(0, fd, re);
}
if ((i = read(fd, cmdline, sizeof(cmdline)-1)) <= 0) {
syslog( LOG_WARNING, "%s: %s: read(%d) failed: errno %d. Ignoring",
conf->prog, proc_file, fd, errno);
return is_sshd_lookup_exit(0, fd, re);
}
if (conf->unconfirmed_regexp) {
if ((reg_ret = regcomp(&regex, conf->unconfirmed_regexp,
REG_EXTENDED|REG_NOSUB))) {
errbuf[0] = 0;
regerror(reg_ret, &regex, errbuf, sizeof(errbuf));
syslog( LOG_ERR, "%s: %s: regcomp() failed: %s", conf->prog,
conf->unconfirmed_regexp, errbuf);
} else {
re = &regex;
}
}
if (re) {
if (!(reg_ret = regexec(re, cmdline, 0, NULL, 0))) {
syslog( LOG_INFO, "%s: %s: Lookup %s", conf->prog, cmdline, name);
return is_sshd_lookup_exit(1, fd, re);
}
} else {
cmdline[i] = 0;
colon = strchr(cmdline, ':');
if (colon && ((0 == strncmp(expected, colon, sizeof(expected) - 1)) ||
(0 == strncmp(accepted, colon, sizeof(accepted) - 1)))) {
syslog( LOG_INFO, "%s: %s: Lookup %s", conf->prog, cmdline, name);
return is_sshd_lookup_exit(1, fd, re);
}
}
if (conf->debug)
syslog( LOG_DEBUG, "%s: %s: Non sshd Lookup %s",
conf->prog, cmdline, name);
return is_sshd_lookup_exit(0, fd, re);
}

View File

@ -0,0 +1,132 @@
/*
Copyright 2020 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* nss_radius_common.h
*
* Common code for NSS for RADIUS cached MPL(Management Privilege Attribute).
* Used by both NSS module(uses the cache) and PAM module(updates the cache)
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <nss.h>
#define RADIUS_MAX_MPL (15)
#define RADIUS_MIN_MPL (1)
#define RADIUS_NSS_CONF "/etc/radius_nss.conf"
#define RADIUS_MAX_NSS_CONF_SZ 2048
#define RADIUS_ATTRIBUTE_CACHE_DIR "/var/cache/radius/user"
#define RADIUS_CACHE_DIR "/var/cache/radius"
#define RADIUS_ATTR_MPL "Management-Privilege-Level"
#define ETC_PASSWD "/etc/passwd"
#define USERADD "/usr/sbin/useradd"
#define USERMOD "/usr/sbin/usermod"
#define USERDEL "/usr/sbin/userdel"
#define BUFLEN 4096
#define UNCONFIRMED_AGEOUT_DEFAULT 600
#define UNCONFIRMED_CLEAR_LIMIT_DEFAULT 10
#define RADIUS_CONFIRMED 0
#define RADIUS_UNCONFIRMED 1
#if defined(TEST_RADIUS_NSS)
#undef RADIUS_NSS_CONF
#define RADIUS_NSS_CONF "radius_nss.conf"
#undef RADIUS_CACHE_DIR
#define RADIUS_CACHE_DIR "."
#undef RADIUS_ATTRIBUTE_CACHE_DIR
#define RADIUS_ATTRIBUTE_CACHE_DIR "user"
#undef ETC_PASSWD
#define ETC_PASSWD "passwd"
#undef USERADD
#define USERADD "/bin/echo"
#undef USERMOD
#define USERMOD "/bin/echo"
#undef USERDEL
#define USERDEL "/bin/echo"
#define syslog(priority,format,...) fprintf(stderr,format"\n",__VA_ARGS__)
#endif
#define STATUS_EPERM 1
#define STATUS_ENOENT 2
#define STATUS_ESRCH 3
#define STATUS_EIO 5
#define STATUS_E2BIG 7
#define STATUS_EINVAL 22
#define STATUS_EFBIG 27
typedef struct _radius_nss_mpl {
gid_t gid;
char * groups; /* Supplementary groups */
char * gecos;
char * shell;
} RADIUS_NSS_MPL;
typedef struct _radius_nss_conf {
char * prog;
int debug;
int trace;
int many_to_one;
int allow_anonymous;
char * unconfirmed_regexp;
int unconfirmed_ageout;
int unconfirmed_clear_limit;
RADIUS_NSS_MPL rnm[RADIUS_MAX_MPL];
} RADIUS_NSS_CONF_B;
int parse_nss_config( RADIUS_NSS_CONF_B * conf, char * prog,
char * file_buf, int file_buf_sz, int * errnop, int * plockfd);
int unparse_nss_config( RADIUS_NSS_CONF_B * conf, int * errnop, int * plockfd);
int radius_lookup_cache( char * prog, const char * nam, int * pmpl);
int radius_fill_pw( RADIUS_NSS_CONF_B * conf, int mpl,
const char * nam, struct passwd * pwd,
char * buffer, size_t buflen, int * errnop);
int radius_copy_pw( RADIUS_NSS_CONF_B * conf, struct passwd * res,
const char * nam, struct passwd * pwd,
char * buffer, size_t buflen, int * errnop);
int is_sshd_lookup(RADIUS_NSS_CONF_B * conf, const char * nam);
int radius_getpwnam_r(char * prog, const char * name,
struct passwd * pwd, char * buf, int buflen, struct passwd ** result);
int radius_update_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl);
int radius_create_user(RADIUS_NSS_CONF_B * conf, const char * user, int mpl,
int unconfirmed);
int radius_clear_unconfirmed_users(RADIUS_NSS_CONF_B * conf);

View File

@ -0,0 +1,19 @@
#######################################################################
#
# Copyright 2019 Broadcom. All rights reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
#
#######################################################################
#
# Restrict rbash
#
if [ "$0" == "rbash" ] ; then
# Prevent from executing arbitrary commands. Only allow CWD commands
# Add shell scripts to the user's home directory to allow them to run
# commands.
PATH=
export PATH
fi

View File

@ -0,0 +1,62 @@
/*
Copyright 2019 Broadcom. All rights reserved.
The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
*/
/*
* Test code for _nss_radius_getpwnam_r(): NSS entry point for getpwnam
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <netdb.h>
#include <nss.h>
int main(int ac, char * av[]) {
enum nss_status status;
struct passwd pw;
char buf[256];
int errno;
char * users[] = { "admin", "user", "netops", "operator", "unknown", 0 };
char ** u;
printf("buf: %p, len: %lx\n", buf, sizeof(buf));
for ( u = users ; *u ; u++) {
status = _nss_radius_getpwnam_r( *u, &pw, buf, sizeof(buf),
&errno);
printf("\n%s: status:%d\n", *u, status);
if (status != NSS_STATUS_SUCCESS)
continue;
printf("\tnam: %s, passwd: %s, uid: %u, gid: %u\n",
pw.pw_name, pw.pw_passwd, pw.pw_uid, pw.pw_gid);
printf("\tnam: %p, passwd: %p, uid: %u, gid: %u\n",
pw.pw_name, pw.pw_passwd, pw.pw_uid, pw.pw_gid);
printf("\n");
printf("\tgecos: %s, dir: %s, shell: %s\n",
pw.pw_gecos, pw.pw_dir, pw.pw_shell);
printf("\tgecos: %p, dir: %p, shell: %p\n",
pw.pw_gecos, pw.pw_dir, pw.pw_shell);
}
return status;
}

0
src/radius/nss/patches/.gitignore vendored Normal file
View File

1
src/radius/pam/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
pam_radius

43
src/radius/pam/Makefile Normal file
View File

@ -0,0 +1,43 @@
.ONESHELL:
SHELL = /bin/bash
.SHELLFLAGS += -e
MAIN_TARGET = libpam-radius-auth_$(PAM_RADIUS_VERSION)_amd64.deb
$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
# Build freeradius-server v1.1.x
pushd freeradius
-rm -rf freeradius-server
git clone https://github.com/FreeRADIUS/freeradius-server.git
popd
pushd freeradius/freeradius-server
git checkout -f 5f715dba4d2dbdb268bf60fcc656352274930941
# Apply patch
patch -p1 < ../patches/freeradius_configure.patch
patch -p1 < ../patches/freeradius_2007-04-06.patch
patch -p1 < ../patches/freeradius_libeap_deprecated_openssl_1_0.patch
./configure --disable-static --enable-libtool-lock
make
popd
# Obtain pam_radius
-rm -rf ./pam_radius
git clone https://github.com/FreeRADIUS/pam_radius.git
pushd ./pam_radius
# This is the latest commit with Radius MPL support
git checkout -f 149c25df84cf5cd0e9addd9346699a9ca8fdddd2
cp -r ../debian .
cp -r ../patches .
# Apply patch
git apply patches/0001-chap-support.patch
git apply patches/0002-peap-mschapv2-support.patch
git apply patches/0003-nas-ip-address-config.patch
dpkg-buildpackage -rfakeroot -b -us -uc -nc
popd
mv $(DERIVED_TARGETS) $* $(DEST)/
$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET)

View File

@ -0,0 +1,5 @@
NOTE: The Debian version of libpam-radius-auth uses as default configuration
file /etc/pam_radius_auth.conf.
Upstream has a default set to /etc/raddb/server that does not fit in Debian.
Be aware that the documentation references has not been changed and they
reflect upstream setups.

View File

@ -0,0 +1,5 @@
libpam-radius-auth (1.4.1-1) unstable; urgency=low
* Initial version.
-- Arun Barboza <29963827+a-barboza@users.noreply.github.com> Tue, 13 Aug 2019 16:46:25 -0700

View File

@ -0,0 +1 @@
10

View File

@ -0,0 +1,16 @@
Source: libpam-radius-auth
Maintainer: Arun Barboza <29963827+a-barboza@users.noreply.github.com>
Section: libs
Priority: extra
Standards-Version: 3.6.2
Build-Depends: libpam0g-dev | libpam-dev, debhelper (>= 10~)
Package: libpam-radius-auth
Architecture: any
Depends: ${shlibs:Depends}
Suggests: radius-server
Description: The PAM RADIUS authentication module
This is the PAM to RADIUS authentication module. It allows any PAM-capable
machine to become a RADIUS client for authentication and accounting
requests. You will, however, need to supply your own RADIUS server to
perform the actual authentication

View File

@ -0,0 +1,284 @@
Please see the individual src files for the copyright
-------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,36 @@
<HTML>
<HEAD>
<TITLE><code>pam_radius_auth</CODE>: The PAM RADIUS authentication module</TITLE>
<BODY BGCOLOR=#FFFFFF TEXT=#000000>
<H1><code>pam_radius_auth</CODE>: The PAM RADIUS authentication module</H1>
This is the PAM to RADIUS authentication module. It allows any
PAM-capable machine to become a RADIUS client for authentication and
accounting requests. You will need a RADIUS server to perform the
actual authentication.
<P><HR>
<H2>Files included with the module</H2>
<A HREF="../README.gz">README</A> <EM>Introduction and documentation</EM><BR>
<A HREF="../examples/INSTALL.gz">INSTALL</A> <EM>Installation instructions</EM><BR>
<A HREF="../USAGE.gz">USAGE</A> <EM>Module configuration and usage documentation</EM><BR>
<A HREF="../changelog.gz">Changelog</A> <EM>What's changed</EM><BR>
<A HREF="../examples/pam_radius_auth.conf">pam_radius_auth.conf</A> <EM>Sample
configuration file for telling the client the location of the RADIUS server.</EM><BR>
<A HREF="../examples/pam_example">Mini Debian HOWTO</A> <EM>C source file</EM><BR>
<A HREF=""></A> <EM></EM><BR>
<P><HR>
<H2>Updates</H2>
For the latest version and updates, see the main web or ftp site:
<P>
<A HREF="http://www.freeradius.org/pam_radius_auth/">http://www.freeradius.org/pam_radius_auth/</A><BR>
<A HREF="ftp://ftp.freeradius.org/pub/radius/pam_radius_auth.tar">ftp://ftp.freeradius.org/pub/radius/pam_radius_auth.tar</A>
<P><HR>
<EM>$Id: index.html,v 1.4 2001/03/30 19:01:56 aland Exp $</EM>
</BODY>
</HTML>

View File

@ -0,0 +1,64 @@
This is a simple and safe example on how to enable radius
authentication to the console login on a Debian system and
you are too lazy to read the USAGE documentation.
Edit /etc/pam.d/login
The default looks like:
[SNIP]
# Disallows other than root logins when /etc/nologin exists
# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth requisite pam_nologin.so
# This module parses /etc/environment (the standard for setting
# environ vars) and also allows you to use an extended config
# file /etc/security/pam_env.conf.
# (Replaces the `ENVIRON_FILE' setting from login.defs)
auth required pam_env.so
# Standard Un*x authentication. The "nullok" line allows passwordless
# accounts.
@include common-auth
[SNIP]
Insert the following line:
auth sufficient pam_radius_auth.so
AFTER
auth required pam_env.so
and BEFORE
# Standard Un*x authentication. The "nullok" line allows passwordless
# accounts.
@include common-auth
so that it will looks like:
[SNIP]
# This module parses /etc/environment (the standard for setting
# environ vars) and also allows you to use an extended config
# file /etc/security/pam_env.conf.
# (Replaces the `ENVIRON_FILE' setting from login.defs)
auth required pam_env.so
##### RADIUS #####
auth sufficient pam_radius_auth.so
# Standard Un*x authentication. The "nullok" line allows passwordless
# accounts.
@include common-auth
[SNIP]
Try now to login in one of the consoles using the radius password.
If it fails the system will prompt again for a password. This time
provide the local one.

116
src/radius/pam/debian/rules Executable file
View File

@ -0,0 +1,116 @@
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
# Build options.
CC=gcc
CFLAGS= -g -Wall -fPIC -DCONF_FILE=\"/etc/pam_radius_auth.conf\"
#LDFLAGS=
#CFLAGS= -g -Wall -fPIC
#LDFLAGS = -shared
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS += -O0
else
CFLAGS += -O2
endif
ifeq ($(DEB_HOST_GNU_CPU),(hppa|m68k|mips|powerpc|s390|sparc|sparc64|sheb))
CFLAGS += -DHIGHFIRST
endif
export CFLAGS
#export LDFLAGS
export CC
build: patch build-stamp
build-stamp:
dh_testdir
# Add here commands to compile the package.
dh_auto_configure -- --enable-pamdir=/lib/$(DEB_HOST_MULTIARCH)/security
# $(MAKE) -e
$(MAKE)
touch build-stamp
#patch:
# if [ ! -f patch-stamp ]; then \
# patch -p1 < debian/patches/001.fix_Makefile.diff && \
# patch -p1 < debian/patches/002.CAN2005-0108.diff && \
# touch patch-stamp; \
# fi
#
#unpatch:
# if [ -f patch-stamp ]; then \
# patch -Rp1 < debian/patches/002.CAN2005-0108.diff && \
# patch -Rp1 < debian/patches/001.fix_Makefile.diff && \
# rm -f patch-stamp; \
# fi
patch:
if [ ! -f patch-stamp ]; then \
touch patch-stamp; \
fi
unpatch:
if [ -f patch-stamp ]; then \
rm -f patch-stamp; \
fi
clean: unpatch real-clean
real-clean:
dh_testdir
dh_testroot
rm -f build-stamp
# Add here commands to clean up after the build process.
[ ! -f Makefile ] || $(MAKE) clean
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs /lib /lib/$(DEB_HOST_MULTIARCH) /lib/$(DEB_HOST_MULTIARCH)/security /etc /etc/logrotate.d /etc/pam_radius_auth.d /etc/pam_radius_auth.d/statistics /usr/share/doc/libpam-radius-auth/html
install -p pam_radius_auth.so debian/libpam-radius-auth/lib/$(DEB_HOST_MULTIARCH)/security/pam_radius_auth.so
install -p ../freeradius/freeradius-server/src/lib/.libs/libradius-1.1.8.so debian/libpam-radius-auth/lib/$(DEB_HOST_MULTIARCH)/libradius-1.1.8.so
install -p ../freeradius/freeradius-server/src/modules/rlm_eap/libeap/.libs/libeap-1.1.8.so debian/libpam-radius-auth/lib/$(DEB_HOST_MULTIARCH)/libeap-1.1.8.so
install -p pam_radius_auth.conf debian/libpam-radius-auth/etc/pam_radius_auth.conf
install -p pam_radius debian/libpam-radius-auth/etc/logrotate.d/pam_radius
install -p index.html debian/libpam-radius-auth/usr/share/doc/libpam-radius-auth/html/index.html
install -p debian/index.html debian/libpam-radius-auth/usr/share/doc/libpam-radius-auth/html/index.debian.html
# Build architecture-independent
binary-indep: build install
# nothing to do
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installchangelogs Changelog
dh_installdocs README.rst TODO USAGE debian/README.Debian
dh_installexamples pam_radius_auth.conf debian/pam_example INSTALL
dh_strip
dh_compress usr/share/doc/libpam-radius-auth/README.rst\
usr/share/doc/libpam-radius-auth/README.Debian\
usr/share/doc/libpam-radius-auth/USAGE\
usr/share/doc/libpam-radius-auth/examples/INSTALL
dh_fixperms
chmod 600 debian/libpam-radius-auth/etc/pam_radius_auth.conf
chmod 644 debian/libpam-radius-auth/etc/logrotate.d/pam_radius
dh_makeshlibs
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-arch binary-indep
.PHONY: build clean binary-arch binary install patch unpatch

2
src/radius/pam/freeradius/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
freeradius-1.1.4
freeradius-server

View File

@ -0,0 +1,113 @@
diff -Naurp freeradius-1.1.4_orig/src/include/radiusd.h freeradius-1.1.4/src/include/radiusd.h
--- freeradius-1.1.4_orig/src/include/radiusd.h 2007-04-06 05:12:44.644347000 -0700
+++ freeradius-1.1.4/src/include/radiusd.h 2007-04-06 05:15:03.427150000 -0700
@@ -218,9 +218,9 @@ typedef struct main_config_t {
REALM *realms;
} MAIN_CONFIG_T;
-#define DEBUG if(debug_flag)log_debug
-#define DEBUG2 if (debug_flag > 1)log_debug
-#define DEBUG3 if (debug_flag > 2)log_debug
+#define DEBUG if(debug_flag) peap_log_debug
+#define DEBUG2 if (debug_flag > 1) peap_log_debug
+#define DEBUG3 if (debug_flag > 2) peap_log_debug
#define SECONDS_PER_DAY 86400
#define MAX_REQUEST_TIME 30
@@ -354,7 +354,7 @@ int radlog(int, const char *, ...)
__attribute__ ((format (printf, 2, 3)))
#endif
;
-int log_debug(const char *, ...)
+int peap_log_debug(const char *, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#endif
--- freeradius-1.1.4_orig/Makefile 2006-06-22 21:31:58.000000000 -0700
+++ freeradius-1.1.4/Makefile 2018-01-16 23:26:04.820218000 -0800
@@ -40,31 +40,16 @@ clean:
#
export DESTDIR := $(R)
install:
- $(INSTALL) -d -m 755 $(R)$(sbindir)
- $(INSTALL) -d -m 755 $(R)$(bindir)
- $(INSTALL) -d -m 755 $(R)$(raddbdir)
- $(INSTALL) -d -m 755 $(R)$(mandir)
- $(INSTALL) -d -m 755 $(R)$(RUNDIR)
- $(INSTALL) -d -m 700 $(R)$(logdir)
- $(INSTALL) -d -m 700 $(R)$(radacctdir)
- $(INSTALL) -d -m 755 $(R)$(datadir)
- $(INSTALL) -d -m 755 $(R)$(dictdir)
- for i in 1 5 8; do \
- $(INSTALL) -d -m 755 $(R)$(mandir)/man$$i; \
- for p in man/man$$i/*.$$i; do \
- $(INSTALL) -m 644 $$p $(R)$(mandir)/man$$i; \
- done \
+ $(INSTALL) -d -m 755 $(PKG_CONFIG_SYSROOT_DIR)$(includedir)/freeradius
+ for p in src/include/*.h \
+ src/modules/rlm_eap/*.h \
+ src/modules/rlm_eap/libeap/*.h ; do \
+ $(INSTALL) -m 644 $$p \
+ $(PKG_CONFIG_SYSROOT_DIR)$(includedir)/freeradius; \
done
- @$(MAKE) $(MFLAGS) WHAT_TO_MAKE=$@ common
- @echo "Installing dictionary files in $(R)$(dictdir)"; \
- cd share; \
- for i in dictionary*; do \
- $(INSTALL) -m 644 $$i $(R)$(dictdir); \
- done
- $(LIBTOOL) --finish $(R)$(libdir)
common:
- @for dir in $(SUBDIRS); do \
+ @for dir in src; do \
echo "Making $(WHAT_TO_MAKE) in $$dir..."; \
$(MAKE) $(MFLAGS) -C $$dir $(WHAT_TO_MAKE) || exit $$?; \
done
diff -Naurp freeradius-1.1.4_orig/src/Makefile freeradius-1.1.4/src/Makefile
--- freeradius-1.1.4_orig/src/Makefile 2006-05-25 09:24:40.000000000 -0700
+++ freeradius-1.1.4/src/Makefile 2018-01-10 13:32:07.572104095 -0800
@@ -20,7 +20,7 @@ install:
@$(MAKE) $(MFLAGS) WHAT_TO_MAKE=$@ common
common:
- @for dir in $(SUBDIRS); do \
+ @for dir in include lib modules; do \
echo "Making $(WHAT_TO_MAKE) in $$dir..."; \
$(MAKE) $(MFLAGS) -C $$dir $(WHAT_TO_MAKE) || exit $$?; \
done
diff -Naurp freeradius-1.1.4_orig/src/modules/Makefile freeradius-1.1.4/src/modules/Makefile
--- freeradius-1.1.4_orig/src/modules/Makefile 2006-07-06 12:16:41.000000000 -0700
+++ freeradius-1.1.4/src/modules/Makefile 2018-01-10 13:35:28.596154618 -0800
@@ -43,7 +43,7 @@ reconfig:
common:
@[ -d lib/ ] || mkdir lib
- @for mod in $(MODULES); do \
+ @for mod in rlm_eap; do \
if [ -d $$mod ] && [ -f $$mod/Makefile ]; then \
echo "Making $(WHAT_TO_MAKE) in $$mod..."; \
$(MAKE) $(MFLAGS) -C $$mod $(WHAT_TO_MAKE) || exit $$?; \
diff -Naurp freeradius-1.1.4_orig/src/modules/rlm_eap/Makefile.in freeradius-1.1.4/src/modules/rlm_eap/Makefile.in
--- freeradius-1.1.4_orig/src/modules/rlm_eap/Makefile.in 2006-05-25 09:24:41.000000000 -0700
+++ freeradius-1.1.4/src/modules/rlm_eap/Makefile.in 2018-01-11 09:46:16.637574778 -0800
@@ -2,8 +2,8 @@
# $Id: Makefile.in,v 1.12.2.2.2.4 2006/05/25 16:24:41 nbk Exp $
#
-TARGET = @targetname@
-SRCS = rlm_eap.c eap.c mem.c state.c
+# TARGET = @targetname@
+# SRCS = rlm_eap.c eap.c mem.c state.c
HEADERS = eap.h rlm_eap.h
RLM_CFLAGS = $(INCLTDL) -Ilibeap
CLIENTLIBS = libeap/libeap.la
@@ -40,7 +40,7 @@ install-subdirs:
$(LIBTOOL) --mode=install $(INSTALL) -m 755 $(INSTALLSTRIP) radeapclient$(EXEEXT) $(R)$(bindir)
common:
- @for dir in $(RLM_SUBDIRS); do \
+ @for dir in libeap; do \
echo "Making $(WHAT_TO_MAKE) in $$dir..."; \
$(MAKE) $(MFLAGS) -C $$dir $(WHAT_TO_MAKE) || exit $$?; \
done

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
diff -Naurp freeradius-1.1.4_orig/src/modules/rlm_eap/libeap/mppe_keys.c freeradius-1.1.4/src/modules/rlm_eap/libeap/mppe_keys.c
--- freeradius-1.1.4_orig/src/modules/rlm_eap/libeap/mppe_keys.c 2019-11-05 17:47:48.118829000 -0800
+++ freeradius-1.1.4/src/modules/rlm_eap/libeap/mppe_keys.c 2019-11-05 19:23:35.176930000 -0800
@@ -52,47 +52,55 @@ static void P_hash(const EVP_MD *evp_md,
const unsigned char *seed, unsigned int seed_len,
unsigned char *out, unsigned int out_len)
{
- HMAC_CTX ctx_a, ctx_out;
+ HMAC_CTX * ctx_a, * ctx_out;
unsigned char a[HMAC_MAX_MD_CBLOCK];
unsigned int size;
- HMAC_CTX_init(&ctx_a);
- HMAC_CTX_init(&ctx_out);
- HMAC_Init_ex(&ctx_a, secret, secret_len, evp_md, NULL);
- HMAC_Init_ex(&ctx_out, secret, secret_len, evp_md, NULL);
+ if (NULL == (ctx_a = HMAC_CTX_new())) {
+ DEBUG("P_hash: HMAC_CTX_new() failed\n");
+ goto P_hash_exit;
+ }
+ if (NULL == (ctx_out = HMAC_CTX_new())) {
+ DEBUG("P_hash: HMAC_CTX_new() failed\n");
+ goto P_hash_exit;
+ }
+
+ HMAC_Init_ex(ctx_a, secret, secret_len, evp_md, NULL);
+ HMAC_Init_ex(ctx_out, secret, secret_len, evp_md, NULL);
- size = HMAC_size(&ctx_out);
+ size = HMAC_size(ctx_out);
/* Calculate A(1) */
- HMAC_Update(&ctx_a, seed, seed_len);
- HMAC_Final(&ctx_a, a, NULL);
+ HMAC_Update(ctx_a, seed, seed_len);
+ HMAC_Final(ctx_a, a, NULL);
while (1) {
/* Calculate next part of output */
- HMAC_Update(&ctx_out, a, size);
- HMAC_Update(&ctx_out, seed, seed_len);
+ HMAC_Update(ctx_out, a, size);
+ HMAC_Update(ctx_out, seed, seed_len);
/* Check if last part */
if (out_len < size) {
- HMAC_Final(&ctx_out, a, NULL);
+ HMAC_Final(ctx_out, a, NULL);
memcpy(out, a, out_len);
break;
}
/* Place digest in output buffer */
- HMAC_Final(&ctx_out, out, NULL);
- HMAC_Init_ex(&ctx_out, NULL, 0, NULL, NULL);
+ HMAC_Final(ctx_out, out, NULL);
+ HMAC_Init_ex(ctx_out, NULL, 0, NULL, NULL);
out += size;
out_len -= size;
/* Calculate next A(i) */
- HMAC_Init_ex(&ctx_a, NULL, 0, NULL, NULL);
- HMAC_Update(&ctx_a, a, size);
- HMAC_Final(&ctx_a, a, NULL);
+ HMAC_Init_ex(ctx_a, NULL, 0, NULL, NULL);
+ HMAC_Update(ctx_a, a, size);
+ HMAC_Final(ctx_a, a, NULL);
}
- HMAC_CTX_cleanup(&ctx_a);
- HMAC_CTX_cleanup(&ctx_out);
+P_hash_exit:
+ if (ctx_a) HMAC_CTX_free(ctx_a);
+ if (ctx_out) HMAC_CTX_free(ctx_out);
memset(a, 0, sizeof(a));
}
@@ -127,21 +135,25 @@ void eaptls_gen_mppe_keys(VALUE_PAIR **r
unsigned char seed[64 + 2*SSL3_RANDOM_SIZE];
unsigned char *p = seed;
size_t prf_size;
+ unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
+ size_t master_key_len;
prf_size = strlen(prf_label);
memcpy(p, prf_label, prf_size);
p += prf_size;
- memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
+ SSL_get_client_random(s, p, SSL3_RANDOM_SIZE);
p += SSL3_RANDOM_SIZE;
prf_size += SSL3_RANDOM_SIZE;
- memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
+ SSL_get_server_random(s, p, SSL3_RANDOM_SIZE);
prf_size += SSL3_RANDOM_SIZE;
- PRF(s->session->master_key, s->session->master_key_length,
- seed, prf_size, out, buf, sizeof(out));
+ master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s),
+ master_key, sizeof(master_key));
+
+ PRF(master_key, master_key_len, seed, prf_size, out, buf, sizeof(out));
p = out;
add_reply(reply_vps, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
@@ -163,14 +175,19 @@ void eapttls_gen_challenge(SSL *s, char
unsigned char out[32], buf[32];
unsigned char seed[sizeof(EAPTLS_PRF_CHALLENGE)-1 + 2*SSL3_RANDOM_SIZE];
unsigned char *p = seed;
+ unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
+ size_t master_key_len;
memcpy(p, EAPTLS_PRF_CHALLENGE, sizeof(EAPTLS_PRF_CHALLENGE)-1);
p += sizeof(EAPTLS_PRF_CHALLENGE)-1;
- memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
+ SSL_get_client_random(s, p, SSL3_RANDOM_SIZE);
p += SSL3_RANDOM_SIZE;
- memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
+ SSL_get_server_random(s, p, SSL3_RANDOM_SIZE);
+
+ master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s),
+ master_key, sizeof(master_key));
- PRF(s->session->master_key, s->session->master_key_length,
+ PRF(master_key, master_key_len,
seed, sizeof(seed), out, buf, sizeof(out));
memcpy(buffer, out, size);

View File

@ -0,0 +1,133 @@
Index: pam_radius/src/pam_radius_auth.c
===================================================================
--- pam_radius.orig/src/pam_radius_auth.c
+++ pam_radius/src/pam_radius_auth.c
@@ -130,6 +130,15 @@ static int _pam_parse(int argc, CONST ch
} else if (!strcmp(*argv, "privilege_level")) {
conf->privilege_level = TRUE;
+ } else if (!strncmp(*argv, "protocol=", 9)) {
+ if (!strncmp((char *)*argv+9, "pap", 4)) {
+ conf->auth_type = AUTH_TYPE_PAP;
+ } else if (!strncmp((char *)*argv+9, "chap", 5)) {
+ conf->auth_type = AUTH_TYPE_CHAP;
+ } else {
+ _pam_log(LOG_WARNING, "ignoring '%s'", *argv);
+ }
+
} else {
_pam_log(LOG_WARNING, "unrecognized option '%s'", *argv);
}
@@ -510,6 +519,61 @@ static void add_password(AUTH_HDR *reque
}
}
+/*
+ * int add_chap_password()
+ *
+ * Description:
+ * Add a RADIUS CHAP-Password attribute to the packet.
+ *
+ * This uses the input password as the secret to construct the
+ * CHAP response. The Request Authenticator field is used as the
+ * CHAP challenge. Compute the response and send as CHAP-Password
+ * in the Access-Request.
+ *
+ * Input(s):
+ * request - pointer to RADIUS request header
+ * chap_id - CHAP ID
+ * password - password string to do CHAP
+ *
+ * Output(s):
+ * request - pointer to RADIUS request header
+ *
+ */
+static void add_chap_password(AUTH_HDR *request, unsigned char chap_id, CONST char *password)
+{
+ unsigned char resp[CHAP_VALUE_LENGTH+1];
+ unsigned char string[MAXPASS+AUTH_VECTOR_LEN+1], *ptr = string;
+ int length;
+ MD5_CTX md5_context;
+ attribute_t *attr;
+
+ length = strlen(password);
+ if (length > MAXPASS) { /* shorten the password for now */
+ _pam_log(LOG_WARNING, "invalid password length: %d. Shortening", length);
+ length = MAXPASS;
+ }
+
+ *ptr++ = chap_id;
+ memcpy(ptr, password, length);
+ ptr += length;
+ memcpy(ptr, request->vector, AUTH_VECTOR_LEN);
+ ptr += AUTH_VECTOR_LEN;
+
+ resp[0] = chap_id;
+ MD5Init(&md5_context);
+ MD5Update(&md5_context, string, ptr - string);
+ MD5Final(resp+1, &md5_context);
+
+ attr = find_attribute(request, PW_CHAP_PASSWORD);
+ if (!attr) {
+ add_attribute(request, PW_CHAP_PASSWORD, resp, AUTH_VECTOR_LEN+1);
+ } else {
+ memcpy(attr->data, resp, AUTH_VECTOR_LEN+1);
+ }
+
+ memset( string, 0, ptr - string); /* Scrub (zeroize) it */
+}
+
static void cleanup(radius_server_t *server)
{
radius_server_t *next;
@@ -804,7 +868,12 @@ static void build_radius_packet(AUTH_HDR
* Add a password, if given.
*/
if (password) {
- add_password(request, PW_PASSWORD, password, conf->server->secret);
+ if ((request->code == PW_AUTHENTICATION_REQUEST)
+ && (conf->auth_type == AUTH_TYPE_CHAP)) {
+ add_chap_password(request, request->vector[0], password);
+ } else {
+ add_password(request, PW_PASSWORD, password, conf->server->secret);
+ }
/*
* Add a NULL password to non-accounting requests.
@@ -1075,7 +1144,12 @@ static int talk_radius(radius_conf_t *co
add_password(request, PW_PASSWORD, password, old_password);
add_password(request, PW_OLD_PASSWORD, old_password, old_password);
} else { /* authentication request */
- add_password(request, PW_PASSWORD, password, server->secret);
+ if ((request->code == PW_AUTHENTICATION_REQUEST)
+ && (conf->auth_type == AUTH_TYPE_CHAP)) {
+ add_chap_password(request, request->vector[0], password);
+ } else {
+ add_password(request, PW_PASSWORD, password, server->secret);
+ }
}
}
}
Index: pam_radius/src/pam_radius_auth.h
===================================================================
--- pam_radius.orig/src/pam_radius_auth.h
+++ pam_radius/src/pam_radius_auth.h
@@ -123,6 +123,10 @@
#endif
+/* Authentication Protocol types. */
+#define AUTH_TYPE_PAP 0
+#define AUTH_TYPE_CHAP 1
+
/*************************************************************************
* Additional RADIUS definitions
*************************************************************************/
@@ -162,6 +166,7 @@ typedef struct radius_conf_t {
char prompt[MAXPROMPT];
int prompt_attribute;
int privilege_level;
+ int auth_type;
} radius_conf_t;
#endif /* PAM_RADIUS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,943 @@
Index: pam_radius/src/pam_radius_auth.c
===================================================================
--- pam_radius.orig/src/pam_radius_auth.c
+++ pam_radius/src/pam_radius_auth.c
@@ -37,6 +37,7 @@
#define PAM_SM_SESSION
#include "pam_radius_auth.h"
+#include "pam_radius_stats.h"
#define DPRINT if (debug) _pam_log
@@ -148,7 +149,54 @@ static int _pam_parse(int argc, CONST ch
} else {
_pam_log(LOG_WARNING, "ignoring '%s'", *argv);
}
+ } else if (!strncmp(*argv, "nas_ip_address=", 15)) {
+ /* Convert it sockaddr.
+ */
+ struct addrinfo hints;
+ struct addrinfo *ai_start;
+ struct addrinfo *ai;
+ int r;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICHOST;
+
+ r = getaddrinfo(*argv+15, NULL, &hints, &ai_start);
+ if (r != 0) {
+ _pam_log(LOG_WARNING, "getaddrinfo() fails. ignoring '%s'", *argv);
+ } else {
+
+ ai = ai_start;
+ while (ai != NULL) {
+ if (ai->ai_family == AF_INET) {
+ memcpy(&(conf->client_ip_storage), ((struct sockaddr_in *)ai->ai_addr),
+ sizeof(struct sockaddr_in));
+ conf->client_ip = (struct sockaddr *) &(conf->client_ip_storage);
+ break;
+ } else if (ai->ai_family == AF_INET6) {
+ memcpy(&(conf->client_ip_storage), ((struct sockaddr_in6 *)ai->ai_addr),
+ sizeof(struct sockaddr_in6));
+ conf->client_ip = (struct sockaddr *) &(conf->client_ip_storage);
+ break;
+ }
+ }
+
+ if (!(conf->client_ip))
+ _pam_log(LOG_WARNING, "Invalid address. ignoring '%s'", *argv);
+ }
+
+ } else if (!strncmp(*argv, "statistics=", 11)) {
+ /* Verify filename doesn't contain '/'.
+ */
+ if (!strchr(*argv+11, '/'))
+ conf->statistics = *argv+11;
+ else
+ _pam_log(LOG_WARNING, "'/' present, ignoring '%s'", *argv);
+
+ } else if (!strncmp(*argv, "trace", 5)) {
+ conf->trace = "pam_radius_trace.log";
} else {
_pam_log(LOG_WARNING, "unrecognized option '%s'", *argv);
}
@@ -424,13 +472,30 @@ static void add_int_attribute(AUTH_HDR *
add_attribute(request, type, (unsigned char *) &value, sizeof(int));
}
-static void add_nas_ip_address(AUTH_HDR *request, char *hostname) {
+static void add_nas_ip_address(AUTH_HDR *request, char *hostname, radius_conf_t * conf) {
struct addrinfo hints;
struct addrinfo *ai_start;
struct addrinfo *ai;
int v4seen = 0, v6seen = 0;
int r;
+ /* If there is a client_ip configured/discovered, use it instead.
+ */
+ if (conf->client_ip) {
+ if ((conf->client_ip)->sa_family == AF_INET) {
+ r = ((struct sockaddr_in *)(conf->client_ip))->sin_addr.s_addr;
+ add_int_attribute(request, PW_NAS_IP_ADDRESS, ntohl(r));
+ v4seen = 1;
+ } else if ((conf->client_ip)->sa_family == AF_INET6) {
+ add_attribute(request, PW_NAS_IPV6_ADDRESS,
+ (unsigned char *) &(((struct sockaddr_in6 *)conf->client_ip)->sin6_addr), 16);
+ v6seen = 1;
+ }
+ }
+
+ if (v4seen || v6seen)
+ return;
+
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
@@ -446,16 +511,25 @@ static void add_nas_ip_address(AUTH_HDR
v4seen = 1;
r = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
add_int_attribute(request, PW_NAS_IP_ADDRESS, ntohl(r));
+ memcpy(&(conf->client_ip_storage), ((struct sockaddr_in *)ai->ai_addr),
+ sizeof(struct sockaddr_in));
+ conf->client_ip = (struct sockaddr *) &(conf->client_ip_storage);
}
if (!v6seen && ai->ai_family == AF_INET6) {
v6seen = 1;
add_attribute(request, PW_NAS_IPV6_ADDRESS,
(unsigned char *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, 16);
+ memcpy(&(conf->client_ip_storage), ((struct sockaddr_in6 *)ai->ai_addr),
+ sizeof(struct sockaddr_in6));
+ conf->client_ip = (struct sockaddr *) &(conf->client_ip_storage);
}
ai = ai->ai_next;
}
freeaddrinfo(ai_start);
+
+ if (!conf->client_ip)
+ conf->client_ip = (struct sockaddr *)(&(conf->client_ip_storage));
}
/*
@@ -893,7 +967,7 @@ static void build_radius_packet(AUTH_HDR
}
/* Perhaps add NAS IP Address (and v6 version) */
- add_nas_ip_address(request, hostname);
+ add_nas_ip_address(request, hostname, conf);
/* There's always a NAS identifier */
if (conf->client_id && *conf->client_id) {
@@ -932,6 +1006,8 @@ static int talk_radius(radius_conf_t *co
int retval;
int sockfd;
socklen_t salen;
+ struct sockaddr_storage saremote;
+ socklen_t saremotelen;
/* ************************************************************ */
/* Now that we're done building the request, we can send it */
@@ -1026,12 +1102,16 @@ static int talk_radius(radius_conf_t *co
total_length = ntohs(request->length);
server_tries = tries;
send:
+ _pam_radius_incr( conf, request->code);
if (server->ip->sa_family == AF_INET) {
salen = sizeof(struct sockaddr_in);
} else {
salen = sizeof(struct sockaddr_in6);
}
+ _pam_radius_trace( conf, TRUE, server->ip,
+ (unsigned char *) request, total_length);
+
/* send the packet */
if (sendto(sockfd, (char *) request, total_length, 0,
server->ip, salen) < 0) {
@@ -1072,9 +1152,11 @@ static int talk_radius(radius_conf_t *co
if (rcode == 0) {
_pam_log(LOG_ERR, "RADIUS server %s failed to respond", server->hostname);
if (--server_tries) {
+ _pam_radius_incr( conf, STATS_RETRY(request->code));
goto send;
}
ok = FALSE;
+ _pam_radius_incr( conf, STATS_TIMEOUT);
break; /* exit from the loop */
} else if (rcode < 0) {
@@ -1111,8 +1193,10 @@ static int talk_radius(radius_conf_t *co
#endif
/* try to receive some data */
+ memset((char *) &saremote, 0, sizeof(saremote));
+ saremotelen = sizeof(saremote);
if ((total_length = recvfrom(sockfd, (void *) response, BUFFER_SIZE,
- 0, NULL, NULL)) < 0) {
+ 0, (struct sockaddr *) &saremote, &saremotelen)) < 0) {
char error_string[BUFFER_SIZE];
get_error_string(errno, error_string, sizeof(error_string));
_pam_log(LOG_ERR, "error reading RADIUS packet from server %s: %s",
@@ -1122,6 +1206,10 @@ static int talk_radius(radius_conf_t *co
/* there's data, see if it's valid */
} else {
+
+ _pam_radius_trace( conf, FALSE, (struct sockaddr *) &saremote,
+ (unsigned char *) response, total_length);
+
char *p = server->secret;
if ((ntohs(response->length) != total_length) ||
@@ -1129,6 +1217,7 @@ static int talk_radius(radius_conf_t *co
_pam_log(LOG_ERR, "RADIUS packet from server %s is corrupted",
server->hostname);
ok = FALSE;
+ _pam_radius_incr( conf, STATS_INVALID_PACKET);
break;
}
@@ -1155,6 +1244,7 @@ static int talk_radius(radius_conf_t *co
_pam_log(LOG_ERR, "packet from RADIUS server %s failed verification: "
"The shared secret is probably incorrect.", server->hostname);
ok = FALSE;
+ _pam_radius_incr( conf, STATS_BAD_AUTHENTICATOR);
break;
}
@@ -1166,6 +1256,7 @@ static int talk_radius(radius_conf_t *co
"request packet ID %d: verification of packet fails",
response->id, request->id);
ok = FALSE;
+ _pam_radius_incr( conf, STATS_INVALID_PACKET);
break;
}
}
@@ -1418,6 +1509,8 @@ PAM_EXTERN int pam_sm_authenticate(pam_h
retval = talk_radius(&config, request, response, password, NULL, config.retries + 1);
PAM_FAIL_CHECK;
+ _pam_radius_incr( &config, response->code);
+
DPRINT(LOG_DEBUG, "Got RADIUS response code %d", response->code);
/*
@@ -1485,6 +1578,8 @@ PAM_EXTERN int pam_sm_authenticate(pam_h
retval = talk_radius(&config, request, response, resp2challenge, NULL, 1);
PAM_FAIL_CHECK;
+ _pam_radius_incr( &config, response->code);
+
DPRINT(LOG_DEBUG, "Got response to challenge code %d", response->code);
/*
@@ -1677,6 +1772,8 @@ static int pam_private_session(pam_handl
retval = talk_radius(&config, request, response, NULL, NULL, 1);
PAM_FAIL_CHECK;
+ _pam_radius_incr( &config, response->code);
+
/* oops! They don't have the right password. Complain and die. */
if (response->code != PW_ACCOUNTING_RESPONSE) {
retval = PAM_PERM_DENIED;
@@ -1792,6 +1889,8 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
retval = talk_radius(&config, request, response, password, NULL, 1);
PAM_FAIL_CHECK;
+ _pam_radius_incr( &config, response->code);
+
/* oops! They don't have the right password. Complain and die. */
if (response->code != PW_AUTHENTICATION_ACK) {
_pam_forget(password);
@@ -1895,6 +1994,8 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
retval = talk_radius(&config, request, response, new_password, password, 1);
PAM_FAIL_CHECK;
+ _pam_radius_incr( &config, response->code);
+
/* Whew! Done password changing, check for password acknowledge */
if (response->code != PW_PASSWORD_ACK) {
retval = PAM_AUTHTOK_ERR;
Index: pam_radius/src/pam_radius_auth.h
===================================================================
--- pam_radius.orig/src/pam_radius_auth.h
+++ pam_radius/src/pam_radius_auth.h
@@ -160,6 +160,8 @@ typedef struct radius_conf_t {
int retries;
int localifdown;
char *client_id;
+ struct sockaddr_storage client_ip_storage;
+ struct sockaddr *client_ip;
int accounting_bug;
int force_prompt;
int max_challenge;
@@ -171,6 +173,9 @@ typedef struct radius_conf_t {
int prompt_attribute;
int privilege_level;
int auth_type;
+#define MAXFILENAME 128
+ CONST char *trace; /* Packet Trace File */
+ CONST char *statistics; /* Statistics File */
} radius_conf_t;
#endif /* PAM_RADIUS_H */
Index: pam_radius/src/radpeapclient.c
===================================================================
--- pam_radius.orig/src/radpeapclient.c
+++ pam_radius/src/radpeapclient.c
@@ -31,6 +31,7 @@
#include "radpeapclient.h"
// #include "mschapv2.h"
// #include "pam_radius_auth.h"
+#include "pam_radius_stats.h"
static unsigned char cached_auth_challenge[16] = { 0 };
static unsigned char cached_peer_challenge[16] = { 0 };
@@ -324,7 +325,7 @@ compose_eap_identity_pkt(RADIUS_PACKET *
/*
* NAS IP address, Remote Access policy dictates this to be present
*/
- switch ((peap_instance.dst_addr)->sa_family) {
+ switch ((peap_instance.client_ip)->sa_family) {
case AF_INET:
@@ -340,7 +341,7 @@ compose_eap_identity_pkt(RADIUS_PACKET *
}
nas_tlv->type = PW_NAS_IP_ADDRESS;
nas_tlv->length = nas_tlv_size;
- nas_ip_addr = ((struct sockaddr_in *)peap_instance.dst_addr)->sin_addr.s_addr;
+ nas_ip_addr = ((struct sockaddr_in *)peap_instance.client_ip)->sin_addr.s_addr;
memcpy(nas_tlv->data, (uint32_t *) & nas_ip_addr, sizeof(uint32_t));
break;
@@ -358,7 +359,7 @@ compose_eap_identity_pkt(RADIUS_PACKET *
}
nas_tlv->type = PW_NAS_IPV6_ADDRESS;
nas_tlv->length = nas_tlv_size;
- nas_ipv6_addr = &(((struct sockaddr_in6 *)peap_instance.dst_addr)->sin6_addr.s6_addr);
+ nas_ipv6_addr = (uint8_t *) &(((struct sockaddr_in6 *)peap_instance.client_ip)->sin6_addr.s6_addr);
memcpy(nas_tlv->data, nas_ipv6_addr, IPV6_ADDR_SIZE);
break;
@@ -729,7 +730,7 @@ compose_eap_packet(RADIUS_PACKET * req,
* NAS IP address, it is need in all packets as
* W2K3 server Remote Access policy is based on this
*/
- switch ((peap_instance.dst_addr)->sa_family) {
+ switch ((peap_instance.client_ip)->sa_family) {
case AF_INET:
@@ -741,11 +742,11 @@ compose_eap_packet(RADIUS_PACKET * req,
peap_log_debug(
"compose_eap_packet:Memory allocation failed,eap_msg is NULL\n");
CLEAN_UP();
- return -1;
+ return 0;
}
nas_tlv->type = PW_NAS_IP_ADDRESS;
nas_tlv->length = nas_tlv_size;
- nas_ip_addr = ((struct sockaddr_in *)peap_instance.dst_addr)->sin_addr.s_addr;
+ nas_ip_addr = ((struct sockaddr_in *)peap_instance.client_ip)->sin_addr.s_addr;
memcpy(nas_tlv->data, (uint32_t *) & nas_ip_addr, sizeof(uint32_t));
break;
@@ -759,11 +760,11 @@ compose_eap_packet(RADIUS_PACKET * req,
peap_log_debug(
"compose_eap_packet:Memory allocation failed,eap_msg is NULL\n");
CLEAN_UP();
- return -1;
+ return 0;
}
nas_tlv->type = PW_NAS_IPV6_ADDRESS;
nas_tlv->length = nas_tlv_size;
- nas_ipv6_addr = &(((struct sockaddr_in6 *)peap_instance.dst_addr)->sin6_addr.s6_addr);
+ nas_ipv6_addr = (uint8_t *) &(((struct sockaddr_in6 *)peap_instance.client_ip)->sin6_addr.s6_addr);
memcpy(nas_tlv->data, nas_ipv6_addr, IPV6_ADDR_SIZE);
break;
@@ -1141,6 +1142,11 @@ rad_peap_send(RADIUS_PACKET * packet, co
sa = (struct sockaddr *) peap_instance.dst_addr;
+ _pam_radius_trace( peap_instance.conf, TRUE, sa,
+ (unsigned char *) packet->data, (int) packet->data_len);
+
+ _pam_radius_incr( peap_instance.conf, packet->code);
+
#ifndef WITH_UDPFROMTO
return sendto(packet->sockfd, packet->data, (int) packet->data_len, 0,
(struct sockaddr *) sa,
@@ -1329,6 +1335,9 @@ static RADIUS_PACKET *rad_recv_ipv6(int
return NULL;
}
+ _pam_radius_trace( peap_instance.conf, FALSE, (struct sockaddr *) &saremote,
+ (unsigned char *) data, (int) packet->data_len);
+
/*
* Fill IP header fields. We need these for the error
* messages which may come later.
@@ -1369,6 +1378,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
peap_log( LOG_ERR, "Malformed RADIUS packet from host %s:"
" too short (received %d < minimum %d)",
remote_ipaddr, packet->data_len, AUTH_HDR_LEN);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1383,6 +1393,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" too long (received %d > maximum %d)",
remote_ipaddr,
packet->data_len, MAX_PACKET_LEN);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1405,6 +1416,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" unknown packet code %d",
remote_ipaddr,
hdr->code);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1425,6 +1437,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" too short (length %d < minimum %d)",
remote_ipaddr,
totallen, AUTH_HDR_LEN);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1441,6 +1454,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" too long (length %d > maximum %d)",
remote_ipaddr,
totallen, MAX_PACKET_LEN);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1458,6 +1472,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" received %d octets, packet length says %d",
remote_ipaddr,
packet->data_len, totallen);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1503,6 +1518,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
"Malformed RADIUS packet from host %s:"
" Invalid attribute 0",
remote_ipaddr);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1517,6 +1533,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" attribute %d too short",
remote_ipaddr,
attr[0]);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1539,6 +1556,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" Message-Authenticator has invalid length %d",
remote_ipaddr,
attr[1] - 2);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1567,6 +1585,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
"Malformed RADIUS packet from host %s:"
" packet attributes do NOT exactly fill the packet",
remote_ipaddr);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1583,6 +1602,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
" (received %d, max %d are allowed).",
remote_ipaddr,
num_attributes, librad_max_attributes);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1601,6 +1621,7 @@ static RADIUS_PACKET *rad_recv_ipv6(int
peap_log( LOG_ERR, "Insecure packet from host %s:"
" Received EAP-Message with no Message-Authenticator.",
remote_ipaddr);
+ _pam_radius_incr( peap_instance.conf, STATS_INVALID_PACKET);
free(packet);
return NULL;
}
@@ -1608,14 +1629,17 @@ static RADIUS_PACKET *rad_recv_ipv6(int
if ((hdr->code > 0) && (hdr->code < 52)) {
peap_log_debug("rad_recv: %s packet from host %s:%d",
packet_codes[hdr->code],
- remote_ipaddr);
+ remote_ipaddr, packet->src_port);
} else {
peap_log_debug("rad_recv: Packet from host %s:%d code=%d",
- remote_ipaddr,
+ remote_ipaddr, packet->src_port,
hdr->code);
}
peap_log_debug(", id=%d, length=%d\n", hdr->id, totallen);
+ if (hdr->code != PW_AUTHENTICATION_ACK)
+ _pam_radius_incr( peap_instance.conf, hdr->code);
+
/*
* Fill RADIUS header fields
*/
@@ -1665,6 +1689,7 @@ rad_peap_recv()
}
if ((rad_decode(rep, 0, peap_instance.shared_secret)) != 0) {
peap_log_debug("rad_peap_recv: rad_decode failed\n");
+ _pam_radius_incr( peap_instance.conf, STATS_BAD_AUTHENTICATOR);
return 0;
}
/*
@@ -2472,8 +2497,10 @@ wait_for_server_response()
peap_log_debug("wait_for_server_response: Server timed out. peap_instance.retriesleft %d \n",
peap_instance.retries);
if (--(peap_instance.retries)) {
+ _pam_radius_incr( peap_instance.conf, STATS_RETRY(STATS_AUTH_REQUEST));
return PEAP_SERVER_RETRY;
} else {
+ _pam_radius_incr( peap_instance.conf, STATS_TIMEOUT);
return PAM_AUTHINFO_UNAVAIL;
}
}
@@ -3203,6 +3230,11 @@ setup_client(const char * user_name, con
peap_instance.retries = conf->retries + 1;
+ /*
+ * Set up peap_instance.client_ip from client_ip.
+ */
+ peap_instance.client_ip = conf->client_ip;
+
#if 0
switch(cur_serv->ip->sa_family) {
@@ -3292,6 +3324,7 @@ pam_peap_authenticate(const char * user_
uint16_t rad_pkt_len = 0;
peap_instance.cur_serv = conf->server;
peap_instance.dst_addr = conf->server->ip;
+ peap_instance.conf = conf;
ret = setup_client(user_name, user_password, conf);
if (0 != ret) {
Index: pam_radius/src/radpeapclient.h
===================================================================
--- pam_radius.orig/src/radpeapclient.h
+++ pam_radius/src/radpeapclient.h
@@ -300,6 +300,8 @@ typedef struct _tls_data_list {
struct _tls_data_list * list_end;
} tls_data_list_t;
+struct radius_conf_t;
+
/*
* The global data structure used for maintaining *all* the
* information needed at any point of time during the
@@ -312,6 +314,8 @@ typedef struct _peap_instance {
#endif
struct sockaddr *dst_addr;
struct sockaddr *src_addr;
+ struct sockaddr *client_ip;
+ struct radius_conf_t * conf;
int sockfd;
char * shared_secret;
char * user_name;
@@ -371,6 +375,8 @@ typedef struct radius_conf_t {
int retries;
int localifdown;
char *client_id;
+ struct sockaddr_storage client_ip_storage;
+ struct sockaddr *client_ip;
int accounting_bug;
int force_prompt;
int max_challenge;
@@ -383,6 +389,8 @@ typedef struct radius_conf_t {
int prompt_attribute;
int privilege_level;
int auth_type;
+ CONST char * trace; /* Packet Trace File */
+ CONST char * statistics; /* Statistics File */
} radius_conf_t;
/*
Index: pam_radius/src/pam_radius_stats.c
===================================================================
--- /dev/null
+++ pam_radius/src/pam_radius_stats.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "pam_radius_auth.h"
+#include "pam_radius_stats.h"
+
+void _pam_radius_incr(radius_conf_t * conf, unsigned int index)
+{
+ unsigned long counters[STATS_MAX_COUNTER];
+ char statistics[MAX_RADIUS_STATS_PATHNAME_LEN];
+ FILE * fp;
+ int fd;
+ int retry_lock;
+
+ if (!conf||!conf->statistics||!index||(index >= STATS_MAX_COUNTER)) {
+ return;
+ }
+
+ /* Open the file.
+ */
+ snprintf(statistics, sizeof(statistics), "%s/%s", STATISTICS_DIR,
+ conf->statistics);
+ statistics[sizeof(statistics) - 1] = 0;
+ if ((fp = fopen(statistics, "r+")) == NULL) {
+ return;
+ }
+
+ /* Lock the file.
+ */
+ fd = fileno(fp);
+ for (retry_lock = 3; retry_lock; retry_lock--) {
+ if (flock(fd, LOCK_EX|LOCK_NB) == 0)
+ break;
+ usleep(250*1000); /* quarter second */
+ }
+ if (!retry_lock) {
+ return;
+ }
+
+ /* Read
+ */
+ memset((char *) (&(counters[0])), 0, sizeof(counters));
+ fscanf(fp,
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld",
+ &(counters[0]), &(counters[1]), &(counters[2]), &(counters[3]),
+ &(counters[4]), &(counters[5]), &(counters[6]), &(counters[7]),
+ &(counters[8]), &(counters[9]), &(counters[10]), &(counters[11]),
+ &(counters[12]), &(counters[13]), &(counters[14]), &(counters[15]),
+ &(counters[16]), &(counters[17]), &(counters[18]), &(counters[19]),
+ &(counters[20]), &(counters[21]), &(counters[22]), &(counters[23]),
+ &(counters[24]), &(counters[25]), &(counters[26]), &(counters[27]),
+ &(counters[28]), &(counters[29]), &(counters[30]), &(counters[31]),
+ &(counters[32]), &(counters[33]), &(counters[34]), &(counters[35])
+ );
+
+ /* Increment
+ */
+ (counters[index])++;
+
+ /* Write
+ */
+ rewind(fp);
+ fprintf(fp,
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld "
+ "%ld %ld %ld %ld %ld %ld",
+ (counters[0]), (counters[1]), (counters[2]), (counters[3]),
+ (counters[4]), (counters[5]), (counters[6]), (counters[7]),
+ (counters[8]), (counters[9]), (counters[10]), (counters[11]),
+ (counters[12]), (counters[13]), (counters[14]), (counters[15]),
+ (counters[16]), (counters[17]), (counters[18]), (counters[19]),
+ (counters[20]), (counters[21]), (counters[22]), (counters[23]),
+ (counters[24]), (counters[25]), (counters[26]), (counters[27]),
+ (counters[28]), (counters[29]), (counters[30]), (counters[31]),
+ (counters[32]), (counters[33]), (counters[34]), (counters[35])
+ );
+
+#if 0 /* Should be released on close */
+ flock(fd, LOCK_UN|LOCK_NB);
+#endif
+
+ fclose(fp);
+
+}
+
+
+#if defined(FOR_TESTING)
+
+radius_conf_t conf;
+
+int main(int ac, char * av[]) {
+
+ conf.statistics = "global";
+
+ _pam_radius_incr(&conf, STATS_AUTH_REQUEST);
+ _pam_radius_incr(&conf, STATS_RETRY(STATS_AUTH_REQUEST));
+ _pam_radius_incr(&conf, STATS_INVALID_PACKET);
+
+}
+
+#endif
Index: pam_radius/src/pam_radius_stats.h
===================================================================
--- /dev/null
+++ pam_radius/src/pam_radius_stats.h
@@ -0,0 +1,43 @@
+
+
+
+#if defined(FOR_TESTING)
+
+typedef struct radius_conf_t {
+ char * statistics; /* Statistics File */
+ char * trace; /* Trace File */
+} radius_conf_t;
+
+#define STATISTICS_DIR "."
+#define TRACE_DIR "."
+
+#endif
+
+#ifndef STATISTICS_DIR
+#define STATISTICS_DIR "/etc/pam_radius_auth.d/statistics"
+#endif
+
+#ifndef TRACE_DIR
+#define TRACE_DIR "/var/log"
+#endif
+
+
+#define STATS_RETRY(code) (16+code)
+
+/* These are the same codes as in the RADIUS packet header */
+#define STATS_AUTH_REQUEST 1
+#define STATS_AUTH_ACK 2
+#define STATS_AUTH_REJECT 3
+
+#define STATS_TIMEOUT 32
+#define STATS_BAD_AUTHENTICATOR 33
+#define STATS_INVALID_PACKET 34
+#define STATS_MAX_COUNTER 36 /* Please change fscanf() fprintf() fmt */
+
+
+#define MAX_RADIUS_STATS_PATHNAME_LEN 256
+#define MAX_RADIUS_TRACE_PATHNAME_LEN 256
+
+void _pam_radius_incr(radius_conf_t * conf, unsigned int index);
+void _pam_radius_trace(radius_conf_t * conf, int send,
+ const struct sockaddr * addr, unsigned char * buf, int len);
Index: pam_radius/src/pam_radius_trace.c
===================================================================
--- /dev/null
+++ pam_radius/src/pam_radius_trace.c
@@ -0,0 +1,156 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include "pam_radius_auth.h"
+#include "pam_radius_stats.h"
+
+void _pam_radius_trace(radius_conf_t * conf, int send,
+ const struct sockaddr * addr, unsigned char * buf, int len)
+{
+ FILE * fp;
+ char trace[MAX_RADIUS_TRACE_PATHNAME_LEN];
+ int fd;
+ int retry_lock;
+#define CTIME_BUFSIZE (26+10) /* GNU library says 26 chars is enough */
+ char ctime_buf[CTIME_BUFSIZE];
+ time_t current_time = 0;
+ char ipVx[ INET6_ADDRSTRLEN + 1 ];
+ int ct, idx;
+ unsigned int port = 0;
+ char port_str[sizeof(":65535")+1];
+
+ if (!conf->trace || (len < 0)) {
+ return;
+ }
+
+ /* Open the file.
+ */
+ snprintf(trace, sizeof(trace), "%s/%s", TRACE_DIR, conf->trace);
+ trace[sizeof(trace) - 1] = 0;
+ if ((fp = fopen(trace, "a")) == NULL) {
+ return;
+ }
+
+ /* Lock the file.
+ */
+ fd = fileno(fp);
+ for (retry_lock = 3; retry_lock; retry_lock--) {
+ if (flock(fd, LOCK_EX|LOCK_NB) == 0)
+ break;
+ usleep(250*1000); /* quarter second */
+ }
+ if (!retry_lock) {
+ return;
+ }
+
+ /* Current Time
+ */
+ time(&current_time);
+ ctime_buf[0] = 0;
+ ctime_r(&current_time, ctime_buf);
+
+ /* Remote Peer
+ */
+ memset(ipVx, 0, sizeof(ipVx));
+ if (addr && (addr->sa_family == AF_INET)) {
+ strncpy(ipVx,
+ inet_ntoa(((struct sockaddr_in *) addr)->sin_addr),
+ sizeof(ipVx) - 1);
+ port = ntohs(((struct sockaddr_in *) addr)->sin_port);
+ } else if (addr && (addr->sa_family == AF_INET6)) {
+ strncpy(ipVx,
+ inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6 *) addr)->sin6_addr),
+ ipVx, sizeof(ipVx)),
+ sizeof(ipVx) - 1);
+ port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port);
+ }
+
+ memset(port_str, 0, sizeof(port_str));
+ if (port)
+ snprintf(port_str, sizeof(port_str) -1, ":%d", port);
+
+ /* Write Summary
+ */
+ fprintf(fp, "[%ld] %s [%s]%s %s", (long) getpid(),
+ (send ? "Sending to" : "Received from"),
+ ipVx, port_str, ctime_buf);
+
+ /* Write Packet
+ */
+ for(ct = 0; ct < len ; ct += 16) {
+ fprintf( fp, "[%ld]", (long) getpid());
+ for (idx = 0; ((ct + idx) < len) && (idx < 16); idx++) {
+ if (idx && ((idx % 8) == 0))
+ fprintf( fp, " ");
+ fprintf( fp, " %02x", (buf[ct + idx]));
+ }
+
+ for ( ; idx < 16 ; idx++) {
+ if (idx && ((idx % 8) == 0))
+ fprintf( fp, " ");
+ fprintf( fp, " ");
+ }
+
+ for (idx = 0; ((ct + idx) < len) && (idx < 16); idx++) {
+ if ((idx % 8) == 0)
+ fprintf( fp, " ");
+ if (isprint(buf[ct + idx]) && !isspace(buf[ct + idx]))
+ fprintf( fp, "%c", buf[ct + idx]);
+ else
+ fprintf( fp, ".");
+ }
+ fprintf( fp, "\n");
+ }
+
+
+ fprintf( fp, "\n");
+
+#if 0 /* Should be released on close */
+ flock(fd, LOCK_UN|LOCK_NB);
+#endif
+
+ fclose(fp);
+
+}
+
+
+
+#if defined(FOR_TESTING)
+
+radius_conf_t conf;
+
+int main(int ac, char * av[]) {
+
+ struct sockaddr_in sa;
+ struct sockaddr_in6 sa6;
+
+ conf.statistics = "global";
+ conf.trace = "packettrace";
+
+ _pam_radius_trace(&conf, 1, NULL, (conf.statistics), strlen(conf.statistics));
+ _pam_radius_trace(&conf, 0, NULL, (conf.trace), strlen(conf.trace));
+
+ memset(&sa, 0, sizeof(sa));
+ memset(&sa6, 0, sizeof(sa6));
+ ((struct sockaddr *)&sa)->sa_family = AF_INET;
+ ((struct sockaddr *)&sa6)->sa_family = AF_INET6;
+ inet_pton(AF_INET, "10.25.1.2", &(sa.sin_addr));
+ inet_pton(AF_INET6, "::1", &(sa6.sin6_addr));
+ sa.sin_port = htons(1812);
+ sa6.sin6_port = htons(1812);
+
+ _pam_radius_trace(&conf, 1, (struct sockaddr *) &sa, "0123456789012345678901234567890123", 34);
+ _pam_radius_trace(&conf, 0, (struct sockaddr *) &sa6, "0123456789012345678901234567890123", 34);
+}
+
+#endif
Index: pam_radius/pam_radius
===================================================================
--- /dev/null
+++ pam_radius/pam_radius
@@ -0,0 +1,9 @@
+/var/log/pam_radius_trace.log
+{
+ size 1M
+ rotate 8
+ missingok
+ notifempty
+ compress
+ delaycompress
+}
Index: pam_radius/Makefile
===================================================================
--- pam_radius.orig/Makefile
+++ pam_radius/Makefile
@@ -63,6 +63,12 @@ src/mschapv2.o: src/mschapv2.c
src/radpeapclient.o: src/radpeapclient.c src/radpeapclient.h
@$(MAKE) -C src $(notdir $@)
+src/pam_radius_stats.o: src/pam_radius_stats.c src/pam_radius_stats.h src/pam_radius_auth.h
+ @$(MAKE) -C src $(notdir $@)
+
+src/pam_radius_trace.o: src/pam_radius_trace.c src/pam_radius_stats.h src/pam_radius_auth.h
+ @$(MAKE) -C src $(notdir $@)
+
#
# This is what should work on Irix:
#pam_radius_auth.so: pam_radius_auth.o md5.o
@@ -81,7 +87,7 @@ src/radpeapclient.o: src/radpeapclient.c
#
# gcc -shared pam_radius_auth.o md5.o -lpam -lc -o pam_radius_auth.so
#
-pam_radius_auth.so: src/pam_radius_auth.o src/md5.o src/mschapv2.o src/radpeapclient.o
+pam_radius_auth.so: src/pam_radius_auth.o src/md5.o src/mschapv2.o src/radpeapclient.o src/pam_radius_stats.o src/pam_radius_trace.o
$(CC) $(LDFLAGS) $^ -lpam -lradius-1.1.8 -leap-1.1.8 -lssl -o pam_radius_auth.so
######################################################################

View File

@ -0,0 +1,3 @@
0001-chap-support.patch
0002-peap-mschapv2-support.patch
0003-nas-ip-address-config.patch

View File

@ -15,6 +15,7 @@ build:
override_dh_installsystemd:
dh_installsystemd --no-start --name=caclmgrd
dh_installsystemd --no-start --name=hostcfgd
dh_installsystemd --no-start --name=aaastatsd
dh_installsystemd --no-start --name=procdockerstatsd
dh_installsystemd --no-start --name=determine-reboot-cause
dh_installsystemd --no-start --name=process-reboot-cause

View File

@ -0,0 +1,17 @@
[Unit]
Description=AAA Statistics Collection daemon
Requires=hostcfgd.service
After=hostcfgd.service updategraph.service
BindsTo=sonic.target
After=sonic.target
[Service]
Type=simple
ExecStart=/usr/local/bin/aaastatsd
Restart=on-failure
RestartSec=10
TimeoutStopSec=3
[Install]
WantedBy=sonic.target

View File

@ -28,6 +28,42 @@ auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not
{% endfor %}
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
{% elif auth['login'] == 'local,radius' %}
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
# For the RADIUS servers, on success jump to the cacheing the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}:{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
{% elif auth['login'] == 'radius,local' %}
# root user can only be authenticated locally. Jump to local.
auth [success={{ (servers | count) }} default=ignore] pam_succeed_if.so user = root
# For the RADIUS servers, on success jump to the cache the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) + 1 - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}:{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
# Local
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
{% elif auth['login'] == 'radius' %}
# root user can only be authenticated locally. Jump to local.
auth [success={{ (servers | count) + 2 }} default=ignore] pam_succeed_if.so user = root
# For the RADIUS servers, on success jump to the cache the MPL(Privilege)
{% for server in servers %}
auth [success={{ (servers | count) - loop.index0 }} new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_radius_auth.so conf=/etc/pam_radius_auth.d/{{ server.ip }}:{{ server.auth_port }}.conf privilege_level protocol={{ server.auth_type }} retry={{ server.retransmit }}{% if server.nas_ip is defined %} nas_ip_address={{ server.nas_ip }}{% endif %}{% if server.nas_id is defined %} client_id={{ server.nas_id }}{% endif %}{% if debug %} debug{% endif %}{% if trace %} trace{% endif %}{% if server.statistics %} statistics={{ server.ip }}{% endif %} try_first_pass
{% endfor %}
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=2 default=ignore] pam_exec.so /usr/sbin/cache_radius
# Local
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die maxtries=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
{% else %}
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass

View File

@ -0,0 +1,3 @@
# server[:port] shared_secret timeout(s) source_ip vrf
[{{ server.ip }}]:{{ server.auth_port }} {{ server.passkey }} {{ server.timeout }} {% if server.src_ip %} {{ server.src_ip }} {% endif %} {% if server.vrf %} {% if not server.src_ip %} - {% endif %} {{ server.vrf }}{% endif %}

View File

@ -0,0 +1,58 @@
#THIS IS AN AUTO-GENERATED FILE
# Generated from: /usr/share/sonic/templates/radius_nss.conf.j2
# RADIUS NSS Configuration File
#
# Debug: on|off|trace
# Default: off
#
# debug=on
{% if debug %}
debug=on
{% endif %}
#
# User Privilege:
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# Eg:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=7;pw_info=netops;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=operator;gid=100;group=docker;shell=/usr/bin/sonic-launch-shell
#
# many_to_one:
# y: Map RADIUS users to one local user per privilege.
# n: Create local user account on first successful authentication.
# Default: n
#
# Eg:
# many_to_one=y
#
# unconfirmed_disallow:
# y: Do not allow unconfirmed users (users created before authentication)
# n: Allow unconfirmed users.
# Default: n
# Eg:
# unconfirmed_disallow=y
#
# unconfirmed_ageout:
# <seconds>: Wait time before purging unconfirmed users
# Default: 600
#
# Eg:
# unconfirmed_ageout=900
#
# unconfirmed_regexp:
# <regexp>: The RE to match the command line of processes for which the
# creation of unconfirmed users are to be allowed.
# Default: (.*: <user> \[priv\])|(.*: \[accepted\])
# where: <user> is the unconfirmed user.
#

View File

@ -2,6 +2,7 @@
*.pyc
scripts/caclmgrdc
scripts/hostcfgdc
scripts/aaastatsdc
scripts/procdockerstatsdc
# Generated by packaging
@ -15,3 +16,7 @@ dist/
.pytest_cache/
coverage.xml
htmlcov/
test-results.xml
# Unit test scratchpad
tests/hostcfgd/output/*

View File

@ -1,2 +1,2 @@
[pytest]
addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml --ignore=tests/hostcfgd/test_vectors.py
addopts = --cov=scripts --cov-report html --cov-report term --cov-report xml --ignore=tests/hostcfgd/test_vectors.py --ignore=tests/hostcfgd/test_radius_vectors.py

View File

@ -0,0 +1,222 @@
#!/usr/bin/env python3
#
import os
import syslog
import threading
from swsssdk import ConfigDBConnector
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# FILE
RADIUS_PAM_AUTH_CONF_DIR = "/etc/pam_radius_auth.d/"
RADIUS_PAM_AUTH_CONF_STATS_DIR = "/etc/pam_radius_auth.d/statistics/"
class RadiusCountersDbMon (threading.Thread):
def __init__(self, ID, name, radiusStatsInstance):
threading.Thread.__init__(self)
self.ID = ID
self.name = name
self.radiusStatsInstance = radiusStatsInstance
def handle_CountersDbRadiusClear(self, key, data):
# print("RadiusCountersDbMon.handle_CountersDbRadiusClear()")
if key == 'clear':
self.radiusStatsInstance.handle_clear()
def run(self):
# print("RadiusCountersDbMon.run()")
self.radiusStatsInstance.counters_db.subscribe('RADIUS', lambda table, key, data: self.handle_CountersDbRadiusClear(key, data))
self.radiusStatsInstance.counters_db.listen()
# print("RadiusCountersDbMon.run(): After listen()")
class RadiusStatsFileHandler(FileSystemEventHandler):
def __init__(self, radiusStatsInstance):
self.radiusStatsInstance = radiusStatsInstance
def on_any_event(self, event):
# print("RadiusStatsFileHandler.on_any_event()")
if event.is_directory:
return None
self.radiusStatsInstance.handle_update(os.path.basename(event.src_path))
class RadiusStatsFileMon ():
def __init__(self, radiusStatsInstance):
self.event_handler = RadiusStatsFileHandler(radiusStatsInstance)
self.observer = Observer()
self.observer.schedule(self.event_handler, RADIUS_PAM_AUTH_CONF_STATS_DIR, recursive=False)
self.observer.start()
# print("RadiusStatsFileMon.__init__(): After observer.start()")
def stop(self):
# print("RadiusStatsFileMon.stop()")
self.observer.stop()
# print("RadiusStatsFileMon.stop(): After observer.stop()")
self.observer.join()
# print("RadiusStatsFileMon.stop(): After observer.join()")
class RadiusStatistics:
def __init__(self, cfg_db, rad_global_conf, radius_conf):
self.radius_counter_names = [
"counter_0",
"access_requests",
"access_accepts",
"access_rejects",
"accounting_requests",
"accounting_responses",
"counter_6",
"counter_7",
"counter_8",
"counter_9",
"counter_10",
"access_challenges",
"counter_12",
"counter_13",
"counter_14",
"counter_15",
"counter_16",
"retried_access_requests",
"counter_18",
"counter_19",
"retried_accounting_requests",
"counter_21",
"counter_22",
"counter_23",
"counter_24",
"counter_25",
"counter_26",
"retried_access_challenges",
"counter_28",
"counter_29",
"counter_30",
"counter_31",
"timeouts",
"bad_authenticators",
"invalid_packets",
"counter_35",
]
self.radius_global = {
'statistics': 'False'
}
self.radius_servers = {}
self.config_db = cfg_db
for row in rad_global_conf:
self.radius_global_update(row, rad_global_conf[row])
for row in radius_conf:
self.radius_server_update(row, radius_conf[row])
self.counters_db = ConfigDBConnector()
self.counters_db.db_connect('COUNTERS_DB', wait_for_init=False,
retry_on=True)
syslog.syslog(syslog.LOG_INFO, 'CountersDB connect success')
self.dbmon_thread = RadiusCountersDbMon("RadiusCountersDbMon",
"RadiusCountersDbMon", self)
self.dbmon_thread.daemon = True
self.dbmon_thread.start()
self.filemon = RadiusStatsFileMon(self)
syslog.syslog(syslog.LOG_INFO, 'RADIUS Stats File Monitor started')
def radius_global_update(self, key, data):
if key == 'global':
self.radius_global.update(data)
for addr in self.radius_servers:
self.create_file(addr)
def radius_server_update(self, key, data):
if data == {}:
if key in self.radius_servers:
del self.radius_servers[key]
else:
self.radius_servers[key] = data
self.create_file(key)
def create_file(self, addr):
# print( "RadiusStatistics.create_file({})".format(addr))
stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + addr
if self.radius_global['statistics'] == 'False':
if os.path.exists(stats_file):
os.unlink(stats_file)
else:
open(stats_file, 'a').close()
os.chmod(stats_file, 0o666)
self.handle_update(addr)
def handle_clear(self):
# print( "RadiusStatistics.handle_clear()")
for filename in os.listdir(RADIUS_PAM_AUTH_CONF_STATS_DIR):
stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + filename
open(stats_file, 'w').close()
def handle_update(self, srv):
# print( "RadiusStatistics.handle_update({})".format(srv))
if self.radius_global['statistics'] == 'False':
return
stats_file = RADIUS_PAM_AUTH_CONF_STATS_DIR + srv
entry = None
if os.path.exists(stats_file):
with open(stats_file, 'r') as f:
lines = f.readlines()
if len(lines) > 0:
radius_counters = lines[0].split(' ')
entry = dict(zip(self.radius_counter_names, radius_counters))
counters_db = ConfigDBConnector()
counters_db.db_connect('COUNTERS_DB', wait_for_init=False,
retry_on=False)
counters_db.set_entry('RADIUS_SERVER_STATS', srv, entry)
counters_db.close(counters_db.COUNTERS_DB)
class AAAStatsDaemon:
def __init__(self):
self.config_db = ConfigDBConnector()
self.config_db.connect(wait_for_init=True, retry_on=True)
syslog.syslog(syslog.LOG_INFO, 'ConfigDB connect success')
radius_global = self.config_db.get_table('RADIUS')
radius_server = self.config_db.get_table('RADIUS_SERVER')
self.radiusstats = RadiusStatistics(self.config_db, radius_global,
radius_server)
def radius_global_handler(self, key, data):
self.radiusstats.radius_global_update(key, data)
def radius_server_handler(self, key, data):
self.radiusstats.radius_server_update(key, data)
def start(self):
self.config_db.subscribe('RADIUS_SERVER',
lambda table, key, data: self.radius_server_handler(key, data))
self.config_db.subscribe('RADIUS',
lambda table, key, data: self.radius_global_handler(key, data))
self.config_db.listen()
# print( "After config_db.listen()")
syslog.syslog(syslog.LOG_INFO, 'Stopping FileMon')
self.radiusstats.filemon.stop()
# print( "Exiting")
syslog.syslog(syslog.LOG_INFO, 'Exiting')
def main():
daemon = AAAStatsDaemon()
daemon.start()
if __name__ == "__main__":
main()

View File

@ -16,20 +16,25 @@ PAM_AUTH_CONF = "/etc/pam.d/common-auth-sonic"
PAM_AUTH_CONF_TEMPLATE = "/usr/share/sonic/templates/common-auth-sonic.j2"
NSS_TACPLUS_CONF = "/etc/tacplus_nss.conf"
NSS_TACPLUS_CONF_TEMPLATE = "/usr/share/sonic/templates/tacplus_nss.conf.j2"
NSS_RADIUS_CONF = "/etc/radius_nss.conf"
NSS_RADIUS_CONF_TEMPLATE = "/usr/share/sonic/templates/radius_nss.conf.j2"
PAM_RADIUS_AUTH_CONF_TEMPLATE = "/usr/share/sonic/templates/pam_radius_auth.conf.j2"
NSS_CONF = "/etc/nsswitch.conf"
ETC_PAMD_SSHD = "/etc/pam.d/sshd"
ETC_PAMD_LOGIN = "/etc/pam.d/login"
# TACACS+
TACPLUS_SERVER_PASSKEY_DEFAULT = ""
TACPLUS_SERVER_TIMEOUT_DEFAULT = "5"
TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap"
def run_cmd(cmd, log_err = True):
try:
subprocess.check_call(cmd, shell = True)
except Exception as err:
if log_err:
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
.format(err.cmd, err.returncode, err.output))
# RADIUS
RADIUS_SERVER_AUTH_PORT_DEFAULT = "1812"
RADIUS_SERVER_PASSKEY_DEFAULT = ""
RADIUS_SERVER_RETRANSMIT_DEFAULT = "3"
RADIUS_SERVER_TIMEOUT_DEFAULT = "5"
RADIUS_SERVER_AUTH_TYPE_DEFAULT = "pap"
RADIUS_PAM_AUTH_CONF_DIR = "/etc/pam_radius_auth.d/"
def is_true(val):
if val == 'True' or val == 'true':
@ -37,6 +42,9 @@ def is_true(val):
else:
return False
def is_vlan_sub_interface(ifname):
ifname_split = ifname.split(".")
return (len(ifname_split) == 2)
def sub(l, start, end):
return l[start:end]
@ -56,6 +64,8 @@ def run_cmd(cmd, log_err = True):
if log_err:
syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}"
.format(err.cmd, err.returncode, err.output))
return err.returncode
return 0
class Iptables(object):
def __init__(self):
@ -144,19 +154,40 @@ class AaaCfg(object):
'timeout': TACPLUS_SERVER_TIMEOUT_DEFAULT,
'passkey': TACPLUS_SERVER_PASSKEY_DEFAULT
}
self.auth = {}
self.tacplus_global = {}
self.tacplus_servers = {}
self.radius_global_default = {
'priority': 0,
'auth_port': RADIUS_SERVER_AUTH_PORT_DEFAULT,
'auth_type': RADIUS_SERVER_AUTH_TYPE_DEFAULT,
'retransmit': RADIUS_SERVER_RETRANSMIT_DEFAULT,
'timeout': RADIUS_SERVER_TIMEOUT_DEFAULT,
'passkey': RADIUS_SERVER_PASSKEY_DEFAULT
}
self.radius_global = {}
self.radius_servers = {}
self.auth = {}
self.debug = False
self.trace = False
self.hostname = ""
# Load conf from ConfigDb
def load(self, aaa_conf, tac_global_conf, tacplus_conf):
def load(self, aaa_conf, tac_global_conf, tacplus_conf, rad_global_conf, radius_conf):
for row in aaa_conf:
self.aaa_update(row, aaa_conf[row], modify_conf=False)
for row in tac_global_conf:
self.tacacs_global_update(row, tac_global_conf[row], modify_conf=False)
for row in tacplus_conf:
self.tacacs_server_update(row, tacplus_conf[row], modify_conf=False)
for row in rad_global_conf:
self.radius_global_update(row, rad_global_conf[row], modify_conf=False)
for row in radius_conf:
self.radius_server_update(row, radius_conf[row], modify_conf=False)
self.modify_conf_file()
def aaa_update(self, key, data, modify_conf=True):
@ -169,6 +200,29 @@ class AaaCfg(object):
if modify_conf:
self.modify_conf_file()
def pick_src_intf_ipaddrs(self, keys, src_intf):
new_ipv4_addr = ""
new_ipv6_addr = ""
for it in keys:
if src_intf != it[0] or (isinstance(it, tuple) == False):
continue
if new_ipv4_addr != "" and new_ipv6_addr != "":
break
ip_str = it[1].split("/")[0]
ip_addr = ipaddress.IPAddress(ip_str)
# Pick the first IP address from the table that matches the source interface
if isinstance(ip_addr, ipaddress.IPv6Address):
if new_ipv6_addr != "":
continue
new_ipv6_addr = ip_str
else:
if new_ipv4_addr != "":
continue
new_ipv4_addr = ip_str
return(new_ipv4_addr, new_ipv6_addr)
def tacacs_global_update(self, key, data, modify_conf=True):
if key == 'global':
self.tacplus_global = data
@ -185,6 +239,106 @@ class AaaCfg(object):
if modify_conf:
self.modify_conf_file()
def handle_radius_source_intf_ip_chg(self, key):
modify_conf=False
if 'src_intf' in self.radius_global:
if key[0] == self.radius_global['src_intf']:
modify_conf=True
for addr in self.radius_servers:
if ('src_intf' in self.radius_servers[addr]) and \
(key[0] == self.radius_servers[addr]['src_intf']):
modify_conf=True
break
if not modify_conf:
return
syslog.syslog(syslog.LOG_INFO, 'RADIUS IP change - key:{}, current server info {}'.format(key, self.radius_servers))
self.modify_conf_file()
def handle_radius_nas_ip_chg(self, key):
modify_conf=False
# Mgmt IP configuration affects only the default nas_ip
if 'nas_ip' not in self.radius_global:
for addr in self.radius_servers:
if 'nas_ip' not in self.radius_servers[addr]:
modify_conf=True
break
if not modify_conf:
return
syslog.syslog(syslog.LOG_INFO, 'RADIUS (NAS) IP change - key:{}, current global info {}'.format(key, self.radius_global))
self.modify_conf_file()
def radius_global_update(self, key, data, modify_conf=True):
if key == 'global':
self.radius_global = data
if 'statistics' in data:
self.radius_global['statistics'] = is_true(data['statistics'])
if modify_conf:
self.modify_conf_file()
def radius_server_update(self, key, data, modify_conf=True):
if data == {}:
if key in self.radius_servers:
del self.radius_servers[key]
else:
self.radius_servers[key] = data
if modify_conf:
self.modify_conf_file()
def hostname_update(self, hostname, modify_conf=True):
if self.hostname == hostname:
return
self.hostname = hostname
# Currently only used for RADIUS
if len(self.radius_servers) == 0:
return
if modify_conf:
self.modify_conf_file()
def get_hostname(self):
return self.hostname
def get_interface_ip(self, source, addr=None):
keys = None
try:
if source.startswith("Eth"):
if is_vlan_sub_interface(source):
keys = self.config_db.get_keys('VLAN_SUB_INTERFACE')
else:
keys = self.config_db.get_keys('INTERFACE')
elif source.startswith("Po"):
if is_vlan_sub_interface(source):
keys = self.config_db.get_keys('VLAN_SUB_INTERFACE')
else:
keys = self.config_db.get_keys('PORTCHANNEL_INTERFACE')
elif source.startswith("Vlan"):
keys = self.config_db.get_keys('VLAN_INTERFACE')
elif source.startswith("Loopback"):
keys = self.config_db.get_keys('LOOPBACK_INTERFACE')
elif source == "eth0":
keys = self.config_db.get_keys('MGMT_INTERFACE')
except Exception as e:
pass
interface_ip = ""
if keys != None:
ipv4_addr, ipv6_addr = self.pick_src_intf_ipaddrs(keys, source)
# Based on the type of addr, return v4 or v6
if addr and isinstance(addr, ipaddress.IPv6Address):
interface_ip = ipv6_addr
else:
# This could be tuned, but that involves a DNS query, so
# offline configuration might trip (or cause delays).
interface_ip = ipv4_addr
return interface_ip
def modify_single_file(self, filename, operations=None):
if operations:
cmd = "sed -e {0} {1} > {1}.new; mv -f {1} {1}.old; mv -f {1}.new {1}".format(' -e '.join(operations), filename)
@ -209,29 +363,82 @@ class AaaCfg(object):
servers_conf.append(server)
servers_conf = sorted(servers_conf, key=lambda t: int(t['priority']), reverse=True)
radius_global = self.radius_global_default.copy()
radius_global.update(self.radius_global)
# RADIUS: Set the default nas_ip, and nas_id
if 'nas_ip' not in radius_global:
nas_ip = self.get_interface_ip("eth0")
if len(nas_ip) > 0:
radius_global['nas_ip'] = nas_ip
if 'nas_id' not in radius_global:
nas_id = self.get_hostname()
if len(nas_id) > 0:
radius_global['nas_id'] = nas_id
radsrvs_conf = []
if self.radius_servers:
for addr in self.radius_servers:
server = radius_global.copy()
server['ip'] = addr
server.update(self.radius_servers[addr])
if 'src_intf' in server:
# RADIUS: Log a message if src_ip is already defined.
if 'src_ip' in server:
syslog.syslog(syslog.LOG_INFO, \
"RADIUS_SERVER|{}: src_intf found. Ignoring src_ip".format(addr))
# RADIUS: If server.src_intf, then get the corresponding
# src_ip based on the server.ip, and set it.
src_ip = self.get_interface_ip(server['src_intf'], addr)
if len(src_ip) > 0:
server['src_ip'] = src_ip
elif 'src_ip' in server:
syslog.syslog(syslog.LOG_INFO, \
"RADIUS_SERVER|{}: src_intf has no usable IP addr.".format(addr))
del server['src_ip']
radsrvs_conf.append(server)
radsrvs_conf = sorted(radsrvs_conf, key=lambda t: int(t['priority']), reverse=True)
template_file = os.path.abspath(PAM_AUTH_CONF_TEMPLATE)
env = jinja2.Environment(loader=jinja2.FileSystemLoader('/'), trim_blocks=True)
env.filters['sub'] = sub
template = env.get_template(template_file)
pam_conf = template.render(auth=auth, src_ip=src_ip, servers=servers_conf)
with open(PAM_AUTH_CONF, 'w') as f:
f.write(pam_conf)
# Modify common-auth include file in /etc/pam.d/login and sshd
if os.path.isfile(PAM_AUTH_CONF):
self.modify_single_file('/etc/pam.d/sshd', [ "'/^@include/s/common-auth$/common-auth-sonic/'" ])
self.modify_single_file('/etc/pam.d/login', [ "'/^@include/s/common-auth$/common-auth-sonic/'" ])
if 'radius' in auth['login']:
pam_conf = template.render(debug=self.debug, trace=self.trace, auth=auth, servers=radsrvs_conf)
else:
self.modify_single_file('/etc/pam.d/sshd', [ "'/^@include/s/common-auth-sonic$/common-auth/'" ])
self.modify_single_file('/etc/pam.d/login', [ "'/^@include/s/common-auth-sonic$/common-auth/'" ])
pam_conf = template.render(auth=auth, src_ip=src_ip, servers=servers_conf)
# Add tacplus in nsswitch.conf if TACACS+ enable
# Use rename(), which is atomic (on the same fs) to avoid empty file
with open(PAM_AUTH_CONF + ".tmp", 'w') as f:
f.write(pam_conf)
os.chmod(PAM_AUTH_CONF + ".tmp", 0o644)
os.rename(PAM_AUTH_CONF + ".tmp", PAM_AUTH_CONF)
# Modify common-auth include file in /etc/pam.d/login, sshd.
# /etc/pam.d/sudo is not handled, because it would change the existing
# behavior. It can be modified once a config knob is added for sudo.
if os.path.isfile(PAM_AUTH_CONF):
self.modify_single_file(ETC_PAMD_SSHD, [ "'/^@include/s/common-auth$/common-auth-sonic/'" ])
self.modify_single_file(ETC_PAMD_LOGIN, [ "'/^@include/s/common-auth$/common-auth-sonic/'" ])
else:
self.modify_single_file(ETC_PAMD_SSHD, [ "'/^@include/s/common-auth-sonic$/common-auth/'" ])
self.modify_single_file(ETC_PAMD_LOGIN, [ "'/^@include/s/common-auth-sonic$/common-auth/'" ])
# Add tacplus/radius in nsswitch.conf if TACACS+/RADIUS enable
if 'tacacs+' in auth['login']:
if os.path.isfile(NSS_CONF):
self.modify_single_file(NSS_CONF, [ "'/^passwd/s/ radius//'" ])
self.modify_single_file(NSS_CONF, [ "'/tacplus/b'", "'/^passwd/s/compat/tacplus &/'", "'/^passwd/s/files/tacplus &/'" ])
elif 'radius' in auth['login']:
if os.path.isfile(NSS_CONF):
self.modify_single_file(NSS_CONF, [ "'/^passwd/s/tacplus //'" ])
self.modify_single_file(NSS_CONF, [ "'/radius/b'", "'/^passwd/s/compat/& radius/'", "'/^passwd/s/files/& radius/'" ])
else:
if os.path.isfile(NSS_CONF):
self.modify_single_file(NSS_CONF, [ "'/^passwd/s/tacplus //g'" ])
self.modify_single_file(NSS_CONF, [ "'/^passwd/s/ radius//'" ])
# Set tacacs+ server in nss-tacplus conf
template_file = os.path.abspath(NSS_TACPLUS_CONF_TEMPLATE)
@ -240,6 +447,41 @@ class AaaCfg(object):
with open(NSS_TACPLUS_CONF, 'w') as f:
f.write(nss_tacplus_conf)
# Set debug in nss-radius conf
template_file = os.path.abspath(NSS_RADIUS_CONF_TEMPLATE)
template = env.get_template(template_file)
nss_radius_conf = template.render(debug=self.debug, trace=self.trace, servers=radsrvs_conf)
with open(NSS_RADIUS_CONF, 'w') as f:
f.write(nss_radius_conf)
# Create the per server pam_radius_auth.conf
if radsrvs_conf:
for srv in radsrvs_conf:
# Configuration File
pam_radius_auth_file = RADIUS_PAM_AUTH_CONF_DIR + srv['ip'] + ":" + srv['auth_port'] + ".conf"
template_file = os.path.abspath(PAM_RADIUS_AUTH_CONF_TEMPLATE)
template = env.get_template(template_file)
pam_radius_auth_conf = template.render(server=srv)
open(pam_radius_auth_file, 'a').close()
os.chmod(pam_radius_auth_file, 0o600)
with open(pam_radius_auth_file, 'w+') as f:
f.write(pam_radius_auth_conf)
# Start the statistics service. Only RADIUS implemented
if ('radius' in auth['login']) and ('statistics' in radius_global) and\
radius_global['statistics']:
cmd = 'service aaastatsd start'
else:
cmd = 'service aaastatsd stop'
syslog.syslog(syslog.LOG_INFO, "cmd - {}".format(cmd))
try:
subprocess.check_call(cmd, shell=True)
except subprocess.CalledProcessError as err:
syslog.syslog(syslog.LOG_ERR,
"{} - failed: return code - {}, output:\n{}"
.format(err.cmd, err.returncode, err.output))
class KdumpCfg(object):
def __init__(self, CfgDb):
self.config_db = CfgDb
@ -398,6 +640,7 @@ class HostConfigDaemon:
self.device_config = {}
self.device_config['DEVICE_METADATA'] = self.config_db.get_table('DEVICE_METADATA')
self.hostname_cache=""
self.aaacfg = AaaCfg()
self.iptables = Iptables()
self.ntpcfg = NtpCfg(self.config_db)
@ -415,7 +658,9 @@ class HostConfigDaemon:
aaa = self.config_db.get_table('AAA')
tacacs_global = self.config_db.get_table('TACPLUS')
tacacs_server = self.config_db.get_table('TACPLUS_SERVER')
self.aaacfg.load(aaa, tacacs_global, tacacs_server)
radius_global = self.config_db.get_table('RADIUS')
radius_server = self.config_db.get_table('RADIUS_SERVER')
self.aaacfg.load(aaa, tacacs_global, tacacs_server, radius_global, radius_server)
lpbk_table = self.config_db.get_table('LOOPBACK_INTERFACE')
self.iptables.load(lpbk_table)
@ -425,6 +670,16 @@ class HostConfigDaemon:
ntp_global = self.config_db.get_table('NTP')
self.ntpcfg.load(ntp_global, ntp_server)
try:
dev_meta = self.config_db.get_table('DEVICE_METADATA')
if 'localhost' in dev_meta:
if 'hostname' in dev_meta['localhost']:
self.hostname_cache = dev_meta['localhost']['hostname']
except Exception as e:
pass
# Update AAA with the hostname
self.aaacfg.hostname_update(self.hostname_cache)
def get_target_state(self, feature_name, state):
template = jinja2.Template(state)
target_state = template.render(self.device_config)
@ -561,6 +816,24 @@ class HostConfigDaemon:
log_data['passkey'] = obfuscate(log_data['passkey'])
syslog.syslog(syslog.LOG_INFO, 'value of {} changed to {}'.format(key, log_data))
def radius_server_handler(self, key, data):
self.aaacfg.radius_server_update(key, data)
log_data = copy.deepcopy(data)
if 'passkey' in log_data:
log_data['passkey'] = obfuscate(log_data['passkey'])
syslog.syslog(syslog.LOG_INFO, 'value of {} changed to {}'.format(key, log_data))
def radius_global_handler(self, key, data):
self.aaacfg.radius_global_update(key, data)
log_data = copy.deepcopy(data)
if 'passkey' in log_data:
log_data['passkey'] = obfuscate(log_data['passkey'])
syslog.syslog(syslog.LOG_INFO, 'value of {} changed to {}'.format(key, log_data))
def mgmt_intf_handler(self, key, data):
self.aaacfg.handle_radius_source_intf_ip_chg(key)
self.aaacfg.handle_radius_nas_ip_chg(key)
def lpbk_handler(self, key, data):
key = ConfigDBConnector.deserialize_key(key)
# Check if delete operation by fetch existing keys
@ -572,6 +845,23 @@ class HostConfigDaemon:
self.iptables.iptables_handler(key, data, add)
self.ntpcfg.handle_ntp_source_intf_chg(key)
self.aaacfg.handle_radius_source_intf_ip_chg(key)
def vlan_intf_handler(self, key, data):
key = ConfigDBConnector.deserialize_key(key)
self.aaacfg.handle_radius_source_intf_ip_chg(key)
def vlan_sub_intf_handler(self, key, data):
key = ConfigDBConnector.deserialize_key(key)
self.aaacfg.handle_radius_source_intf_ip_chg(key)
def portchannel_intf_handler(self, key, data):
key = ConfigDBConnector.deserialize_key(key)
self.aaacfg.handle_radius_source_intf_ip_chg(key)
def phy_intf_handler(self, key, data):
key = ConfigDBConnector.deserialize_key(key)
self.aaacfg.handle_radius_source_intf_ip_chg(key)
def feature_state_handler(self, key, data):
feature_name = key
@ -617,7 +907,14 @@ class HostConfigDaemon:
self.config_db.subscribe('AAA', lambda table, key, data: self.aaa_handler(key, data))
self.config_db.subscribe('TACPLUS_SERVER', lambda table, key, data: self.tacacs_server_handler(key, data))
self.config_db.subscribe('TACPLUS', lambda table, key, data: self.tacacs_global_handler(key, data))
self.config_db.subscribe('RADIUS_SERVER', lambda table, key, data: self.radius_server_handler(key, data))
self.config_db.subscribe('RADIUS', lambda table, key, data: self.radius_global_handler(key, data))
self.config_db.subscribe('MGMT_INTERFACE', lambda table, key, data: self.mgmt_intf_handler(key, data))
self.config_db.subscribe('LOOPBACK_INTERFACE', lambda table, key, data: self.lpbk_handler(key, data))
self.config_db.subscribe('VLAN_INTERFACE', lambda table, key, data: self.vlan_intf_handler(key, data))
self.config_db.subscribe('VLAN_SUB_INTERFACE', lambda table, key, data: self.vlan_sub_intf_handler(key, data))
self.config_db.subscribe('PORTCHANNEL_INTERFACE', lambda table, key, data: self.portchannel_intf_handler(key, data))
self.config_db.subscribe('INTERFACE', lambda table, key, data: self.phy_intf_handler(key, data))
self.config_db.subscribe('FEATURE', lambda table, key, data: self.feature_state_handler(key, data))
self.config_db.subscribe('NTP_SERVER', lambda table, key, data: self.ntp_server_handler(key, data))
self.config_db.subscribe('NTP', lambda table, key, data: self.ntp_global_handler(key, data))

View File

@ -16,6 +16,7 @@ setup(
scripts = [
'scripts/caclmgrd',
'scripts/hostcfgd',
'scripts/aaastatsd',
'scripts/procdockerstatsd',
'scripts/determine-reboot-cause',
'scripts/process-reboot-cause',

View File

@ -0,0 +1,102 @@
import importlib.machinery
import importlib.util
import filecmp
import shutil
import os
import sys
import subprocess
import swsssdk
from parameterized import parameterized
from unittest import TestCase, mock
from tests.hostcfgd.test_radius_vectors import HOSTCFGD_TEST_RADIUS_VECTOR
from tests.hostcfgd.mock_configdb import MockConfigDb
swsssdk.ConfigDBConnector = MockConfigDb
test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")
src_path = os.path.dirname(modules_path)
templates_path = os.path.join(src_path, "sonic-host-services-data/templates")
output_path = os.path.join(test_path, "hostcfgd/output")
sample_output_path = os.path.join(test_path, "hostcfgd/sample_output")
sys.path.insert(0, modules_path)
# Load the file under test
hostcfgd_path = os.path.join(scripts_path, 'hostcfgd')
loader = importlib.machinery.SourceFileLoader('hostcfgd', hostcfgd_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
hostcfgd = importlib.util.module_from_spec(spec)
loader.exec_module(hostcfgd)
sys.modules['hostcfgd'] = hostcfgd
class TestHostcfgdRADIUS(TestCase):
"""
Test hostcfd daemon - RADIUS
"""
def run_diff(self, file1, file2):
return subprocess.check_output('diff -uR {} {} || true'.format(file1, file2), shell=True)
@parameterized.expand(HOSTCFGD_TEST_RADIUS_VECTOR)
def test_hostcfgd_radius(self, test_name, test_data):
"""
Test RADIUS hostcfd daemon initialization
Args:
test_name(str): test name
test_data(dict): test data which contains initial Config Db tables, and expected results
Returns:
None
"""
t_path = templates_path
op_path = output_path + "/" + test_name
sop_path = sample_output_path + "/" + test_name
hostcfgd.PAM_AUTH_CONF_TEMPLATE = t_path + "/common-auth-sonic.j2"
hostcfgd.NSS_TACPLUS_CONF_TEMPLATE = t_path + "/tacplus_nss.conf.j2"
hostcfgd.NSS_RADIUS_CONF_TEMPLATE = t_path + "/radius_nss.conf.j2"
hostcfgd.PAM_RADIUS_AUTH_CONF_TEMPLATE = t_path + "/pam_radius_auth.conf.j2"
hostcfgd.PAM_AUTH_CONF = op_path + "/common-auth-sonic"
hostcfgd.NSS_TACPLUS_CONF = op_path + "/tacplus_nss.conf"
hostcfgd.NSS_RADIUS_CONF = op_path + "/radius_nss.conf"
hostcfgd.NSS_CONF = op_path + "/nsswitch.conf"
hostcfgd.ETC_PAMD_SSHD = op_path + "/sshd"
hostcfgd.ETC_PAMD_LOGIN = op_path + "/login"
hostcfgd.RADIUS_PAM_AUTH_CONF_DIR = op_path + "/"
shutil.rmtree( op_path, ignore_errors=True)
os.mkdir( op_path)
shutil.copyfile( sop_path + "/sshd.old", op_path + "/sshd")
shutil.copyfile( sop_path + "/login.old", op_path + "/login")
MockConfigDb.set_config_db(test_data["config_db"])
host_config_daemon = hostcfgd.HostConfigDaemon()
aaa = host_config_daemon.config_db.get_table('AAA')
try:
radius_global = host_config_daemon.config_db.get_table('RADIUS')
except:
radius_global = []
try:
radius_server = \
host_config_daemon.config_db.get_table('RADIUS_SERVER')
except:
radius_server = []
host_config_daemon.aaacfg.load(aaa,[],[],radius_global,radius_server)
dcmp = filecmp.dircmp(sop_path, op_path)
diff_output = ""
for name in dcmp.diff_files:
diff_output += \
"Diff: file: {} expected: {} output: {}\n".format(\
name, dcmp.left, dcmp.right)
diff_output += self.run_diff( dcmp.left + "/" + name,\
dcmp.right + "/" + name)
self.assertTrue(len(diff_output) == 0, diff_output)

View File

@ -0,0 +1,4 @@
# Ignore all test generated files
*
# But keep this file
!.gitignore

View File

@ -0,0 +1,21 @@
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
#
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)

View File

@ -0,0 +1,116 @@
#
# The PAM configuration file for the Shadow `login' service
#
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth optional pam_faildelay.so delay=3000000
# Outputs an issue file prior to each login prompt (Replaces the
# ISSUE_FILE option from login.defs). Uncomment for use
# auth required pam_issue.so issue=/etc/issue
# Disallows root logins except on tty's listed in /etc/securetty
# (Replaces the `CONSOLE' setting from login.defs)
#
# With the default control of this module:
# [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die]
# root will not be prompted for a password on insecure lines.
# if an invalid username is entered, a password is prompted (but login
# will eventually be rejected)
#
# You can change it to a "requisite" module if you think root may mis-type
# her login and should not be prompted for a password in that case. But
# this will leave the system as vulnerable to user enumeration attacks.
#
# You can change it to a "required" module if you think it permits to
# guess valid user names of your system (invalid user names are considered
# as possibly being root on insecure lines), but root passwords may be
# communicated over insecure lines.
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
# Disallows other than root logins when /etc/nologin exists
# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible
# that a module could execute code in the wrong domain.
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# SELinux needs to intervene at login time to ensure that the process
# starts in the proper default security context. Only sessions which are
# intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth-sonic
# This allows certain extra groups to be granted to a user
# based on things like time of day, tty, service, and user.
# Please edit /etc/security/group.conf to fit your needs
# (Replaces the `CONSOLE_GROUPS' option in login.defs)
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set
# time restraint on logins.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# Uncomment and edit /etc/security/access.conf if you need to
# set access limits.
# (Replaces /etc/login.access file)
# account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# Prints the last login info upon successful login
# (Replaces the `LASTLOG_ENAB' option from login.defs)
session optional pam_lastlog.so
# Prints the message of the day upon successful login.
# (Replaces the `MOTD_FILE' option in login.defs)
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password

View File

@ -0,0 +1,116 @@
#
# The PAM configuration file for the Shadow `login' service
#
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth optional pam_faildelay.so delay=3000000
# Outputs an issue file prior to each login prompt (Replaces the
# ISSUE_FILE option from login.defs). Uncomment for use
# auth required pam_issue.so issue=/etc/issue
# Disallows root logins except on tty's listed in /etc/securetty
# (Replaces the `CONSOLE' setting from login.defs)
#
# With the default control of this module:
# [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die]
# root will not be prompted for a password on insecure lines.
# if an invalid username is entered, a password is prompted (but login
# will eventually be rejected)
#
# You can change it to a "requisite" module if you think root may mis-type
# her login and should not be prompted for a password in that case. But
# this will leave the system as vulnerable to user enumeration attacks.
#
# You can change it to a "required" module if you think it permits to
# guess valid user names of your system (invalid user names are considered
# as possibly being root on insecure lines), but root passwords may be
# communicated over insecure lines.
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
# Disallows other than root logins when /etc/nologin exists
# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible
# that a module could execute code in the wrong domain.
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# SELinux needs to intervene at login time to ensure that the process
# starts in the proper default security context. Only sessions which are
# intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth
# This allows certain extra groups to be granted to a user
# based on things like time of day, tty, service, and user.
# Please edit /etc/security/group.conf to fit your needs
# (Replaces the `CONSOLE_GROUPS' option in login.defs)
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set
# time restraint on logins.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# Uncomment and edit /etc/security/access.conf if you need to
# set access limits.
# (Replaces /etc/login.access file)
# account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# Prints the last login info upon successful login
# (Replaces the `LASTLOG_ENAB' option from login.defs)
session optional pam_lastlog.so
# Prints the message of the day upon successful login.
# (Replaces the `MOTD_FILE' option in login.defs)
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password

View File

@ -0,0 +1,56 @@
#THIS IS AN AUTO-GENERATED FILE
# Generated from: /usr/share/sonic/templates/radius_nss.conf.j2
# RADIUS NSS Configuration File
#
# Debug: on|off|trace
# Default: off
#
# debug=on
debug=on
#
# User Privilege:
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# Eg:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=7;pw_info=netops;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=operator;gid=100;group=docker;shell=/usr/bin/sonic-launch-shell
#
# many_to_one:
# y: Map RADIUS users to one local user per privilege.
# n: Create local user account on first successful authentication.
# Default: n
#
# Eg:
# many_to_one=y
#
# unconfirmed_disallow:
# y: Do not allow unconfirmed users (users created before authentication)
# n: Allow unconfirmed users.
# Default: n
# Eg:
# unconfirmed_disallow=y
#
# unconfirmed_ageout:
# <seconds>: Wait time before purging unconfirmed users
# Default: 600
#
# Eg:
# unconfirmed_ageout=900
#
# unconfirmed_regexp:
# <regexp>: The RE to match the command line of processes for which the
# creation of unconfirmed users are to be allowed.
# Default: (.*: <user> \[priv\])|(.*: \[accepted\])
# where: <user> is the unconfirmed user.
#

View File

@ -0,0 +1,55 @@
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth-sonic
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password

View File

@ -0,0 +1,55 @@
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password

View File

@ -0,0 +1,23 @@
# Configuration for libnss-tacplus
# debug - If you want to open debug log, set it on
# Default: off
# debug=on
debug=on
# src_ip - set source address of TACACS+ protocol packets
# Default: None (auto source ip address)
# src_ip=2.2.2.2
# server - set ip address, tcp port, secret string and timeout for TACACS+ servers
# Default: None (no TACACS+ server)
# server=1.1.1.1:49,secret=test,timeout=3
# user_priv - set the map between TACACS+ user privilege and local user's passwd
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/bin/bash
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/bin/bash
# many_to_one - create one local user for many TACACS+ users which has the same privilege
# Default: many_to_one=n
# many_to_one=y

View File

@ -0,0 +1,2 @@
# server[:port] shared_secret timeout(s) source_ip vrf
[10.10.10.1]:1645 pass1 1

View File

@ -0,0 +1,2 @@
# server[:port] shared_secret timeout(s) source_ip vrf
[10.10.10.2]:1645 pass2 2

View File

@ -0,0 +1,30 @@
#THIS IS AN AUTO-GENERATED FILE
#
# /etc/pam.d/common-auth- authentication settings common to all services
# This file is included from other service-specific PAM config files,
# and should contain a list of the authentication modules that define
# the central authentication scheme for use on the system
# (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the
# traditional Unix authentication mechanisms.
#
# here are the per-package modules (the "Primary" block)
# root user can only be authenticated locally. Jump to local.
auth [success=2 default=ignore] pam_succeed_if.so user = root
# For the RADIUS servers, on success jump to the cache the MPL(Privilege)
auth [success=3 new_authtok_reqd=done default=ignore auth_err=die] pam_radius_auth.so conf=/etc/pam_radius_auth.d/10.10.10.1:1645.conf privilege_level protocol=pap retry=1 nas_ip_address=10.10.10.10 debug try_first_pass
auth [success=2 new_authtok_reqd=done default=ignore auth_err=die] pam_radius_auth.so conf=/etc/pam_radius_auth.d/10.10.10.2:1645.conf privilege_level protocol=chap retry=2 nas_ip_address=10.10.10.10 debug try_first_pass
# Local
auth [success=done new_authtok_reqd=done default=ignore auth_err=die maxtries=die] pam_unix.so nullok try_first_pass
auth requisite pam_deny.so
# Cache MPL(Privilege)
auth [success=1 default=ignore] pam_exec.so /usr/sbin/cache_radius
#
# here's the fallback if no module succeeds
auth requisite pam_deny.so
# prime the stack with a positive return value if there isn't one already;
# this avoids us returning an error just because nothing sets a success code
# since the modules above will each just jump around
auth required pam_permit.so
# and here are more per-package modules (the "Additional" block)

View File

@ -0,0 +1,116 @@
#
# The PAM configuration file for the Shadow `login' service
#
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth optional pam_faildelay.so delay=3000000
# Outputs an issue file prior to each login prompt (Replaces the
# ISSUE_FILE option from login.defs). Uncomment for use
# auth required pam_issue.so issue=/etc/issue
# Disallows root logins except on tty's listed in /etc/securetty
# (Replaces the `CONSOLE' setting from login.defs)
#
# With the default control of this module:
# [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die]
# root will not be prompted for a password on insecure lines.
# if an invalid username is entered, a password is prompted (but login
# will eventually be rejected)
#
# You can change it to a "requisite" module if you think root may mis-type
# her login and should not be prompted for a password in that case. But
# this will leave the system as vulnerable to user enumeration attacks.
#
# You can change it to a "required" module if you think it permits to
# guess valid user names of your system (invalid user names are considered
# as possibly being root on insecure lines), but root passwords may be
# communicated over insecure lines.
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
# Disallows other than root logins when /etc/nologin exists
# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible
# that a module could execute code in the wrong domain.
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# SELinux needs to intervene at login time to ensure that the process
# starts in the proper default security context. Only sessions which are
# intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth-sonic
# This allows certain extra groups to be granted to a user
# based on things like time of day, tty, service, and user.
# Please edit /etc/security/group.conf to fit your needs
# (Replaces the `CONSOLE_GROUPS' option in login.defs)
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set
# time restraint on logins.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# Uncomment and edit /etc/security/access.conf if you need to
# set access limits.
# (Replaces /etc/login.access file)
# account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# Prints the last login info upon successful login
# (Replaces the `LASTLOG_ENAB' option from login.defs)
session optional pam_lastlog.so
# Prints the message of the day upon successful login.
# (Replaces the `MOTD_FILE' option in login.defs)
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password

View File

@ -0,0 +1,116 @@
#
# The PAM configuration file for the Shadow `login' service
#
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth optional pam_faildelay.so delay=3000000
# Outputs an issue file prior to each login prompt (Replaces the
# ISSUE_FILE option from login.defs). Uncomment for use
# auth required pam_issue.so issue=/etc/issue
# Disallows root logins except on tty's listed in /etc/securetty
# (Replaces the `CONSOLE' setting from login.defs)
#
# With the default control of this module:
# [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die]
# root will not be prompted for a password on insecure lines.
# if an invalid username is entered, a password is prompted (but login
# will eventually be rejected)
#
# You can change it to a "requisite" module if you think root may mis-type
# her login and should not be prompted for a password in that case. But
# this will leave the system as vulnerable to user enumeration attacks.
#
# You can change it to a "required" module if you think it permits to
# guess valid user names of your system (invalid user names are considered
# as possibly being root on insecure lines), but root passwords may be
# communicated over insecure lines.
auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
# Disallows other than root logins when /etc/nologin exists
# (Replaces the `NOLOGINS_FILE' option from login.defs)
auth requisite pam_nologin.so
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible
# that a module could execute code in the wrong domain.
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Sets the loginuid process attribute
session required pam_loginuid.so
# SELinux needs to intervene at login time to ensure that the process
# starts in the proper default security context. Only sessions which are
# intended to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# When the module is present, "required" would be sufficient (When SELinux
# is disabled, this returns success.)
# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
#
# parsing /etc/environment needs "readenv=1"
session required pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session required pam_env.so readenv=1 envfile=/etc/default/locale
# Standard Un*x authentication.
@include common-auth
# This allows certain extra groups to be granted to a user
# based on things like time of day, tty, service, and user.
# Please edit /etc/security/group.conf to fit your needs
# (Replaces the `CONSOLE_GROUPS' option in login.defs)
auth optional pam_group.so
# Uncomment and edit /etc/security/time.conf if you need to set
# time restraint on logins.
# (Replaces the `PORTTIME_CHECKS_ENAB' option from login.defs
# as well as /etc/porttime)
# account requisite pam_time.so
# Uncomment and edit /etc/security/access.conf if you need to
# set access limits.
# (Replaces /etc/login.access file)
# account required pam_access.so
# Sets up user limits according to /etc/security/limits.conf
# (Replaces the use of /etc/limits in old login)
session required pam_limits.so
# Prints the last login info upon successful login
# (Replaces the `LASTLOG_ENAB' option from login.defs)
session optional pam_lastlog.so
# Prints the message of the day upon successful login.
# (Replaces the `MOTD_FILE' option in login.defs)
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session optional pam_mail.so standard
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x account and session
@include common-account
@include common-session
@include common-password

View File

@ -0,0 +1,56 @@
#THIS IS AN AUTO-GENERATED FILE
# Generated from: /usr/share/sonic/templates/radius_nss.conf.j2
# RADIUS NSS Configuration File
#
# Debug: on|off|trace
# Default: off
#
# debug=on
debug=on
#
# User Privilege:
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# Eg:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/usr/bin/sonic-launch-shell
# user_priv=7;pw_info=netops;gid=999;group=docker;shell=/usr/bin/sonic-launch-shell
# user_priv=1;pw_info=operator;gid=100;group=docker;shell=/usr/bin/sonic-launch-shell
#
# many_to_one:
# y: Map RADIUS users to one local user per privilege.
# n: Create local user account on first successful authentication.
# Default: n
#
# Eg:
# many_to_one=y
#
# unconfirmed_disallow:
# y: Do not allow unconfirmed users (users created before authentication)
# n: Allow unconfirmed users.
# Default: n
# Eg:
# unconfirmed_disallow=y
#
# unconfirmed_ageout:
# <seconds>: Wait time before purging unconfirmed users
# Default: 600
#
# Eg:
# unconfirmed_ageout=900
#
# unconfirmed_regexp:
# <regexp>: The RE to match the command line of processes for which the
# creation of unconfirmed users are to be allowed.
# Default: (.*: <user> \[priv\])|(.*: \[accepted\])
# where: <user> is the unconfirmed user.
#

View File

@ -0,0 +1,55 @@
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth-sonic
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password

View File

@ -0,0 +1,55 @@
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
@include common-auth
# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
# account required pam_access.so
# Standard Un*x authorization.
@include common-account
# SELinux needs to be the first session rule. This ensures that any
# lingering context has been cleared. Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
# Set the loginuid process attribute.
session required pam_loginuid.so
# Create a new session keyring.
session optional pam_keyinit.so force revoke
# Standard Un*x session setup and teardown.
@include common-session
# Print the message of the day upon successful login.
# This includes a dynamically generated part from /run/motd.dynamic
# and a static (admin-editable) part from /etc/motd.
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session required pam_env.so # [1]
# In Debian 4.0 (etch), locale-related environment variables were moved to
# /etc/default/locale, so read that as well.
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context. Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
# Standard Un*x password updating.
@include common-password

View File

@ -0,0 +1,23 @@
# Configuration for libnss-tacplus
# debug - If you want to open debug log, set it on
# Default: off
# debug=on
debug=on
# src_ip - set source address of TACACS+ protocol packets
# Default: None (auto source ip address)
# src_ip=2.2.2.2
# server - set ip address, tcp port, secret string and timeout for TACACS+ servers
# Default: None (no TACACS+ server)
# server=1.1.1.1:49,secret=test,timeout=3
# user_priv - set the map between TACACS+ user privilege and local user's passwd
# Default:
# user_priv=15;pw_info=remote_user_su;gid=1000;group=sudo,docker;shell=/bin/bash
# user_priv=1;pw_info=remote_user;gid=999;group=docker;shell=/bin/bash
# many_to_one - create one local user for many TACACS+ users which has the same privilege
# Default: many_to_one=n
# many_to_one=y

View File

@ -0,0 +1,181 @@
from unittest.mock import call
"""
hostcfgd test radius vector
"""
HOSTCFGD_TEST_RADIUS_VECTOR = [
[
"RADIUS",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"hostname": "radius",
}
},
"FEATURE": {
"dhcp_relay": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"set_owner": "kube",
"state": "enabled"
},
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"AAA": {
"authentication": {
"login": "radius,local",
"debug": "True",
}
},
"RADIUS": {
"global": {
"nas_ip": "10.10.10.10",
"auth_port": "1645",
"auth_type": "mschapv2",
"retransmit": "2",
"timeout": "3",
"passkey": "pass",
}
},
"RADIUS_SERVER": {
"10.10.10.1": {
"auth_type": "pap",
"retransmit": "1",
"timeout": "1",
"passkey": "pass1",
},
"10.10.10.2": {
"auth_type": "chap",
"retransmit": "2",
"timeout": "2",
"passkey": "pass2",
}
},
},
"expected_config_db": {
"DEVICE_METADATA": {
"localhost": {
"hostname": "radius",
}
},
"FEATURE": {
"dhcp_relay": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"set_owner": "kube",
"state": "enabled"
},
},
"AAA": {
"authentication": {
"login": "radius,local",
"debug": "True",
}
},
"RADIUS": {
"global": {
"nas_ip": "10.10.10.10",
"auth_port": "1645",
"auth_type": "mschapv2",
"retransmit": "2",
"timeout": "3",
"passkey": "pass",
}
},
"RADIUS_SERVER": {
"10.10.10.1": {
"auth_type": "pap",
"retransmit": "1",
"timeout": "1",
"passkey": "pass1",
},
"10.10.10.2": {
"auth_type": "chap",
"retransmit": "2",
"timeout": "2",
"passkey": "pass2",
}
},
},
"expected_subprocess_calls": [
call("service aaastatsd start", shell=True),
],
}
],
[
"LOCAL",
{
"config_db": {
"DEVICE_METADATA": {
"localhost": {
"hostname": "local",
}
},
"FEATURE": {
"dhcp_relay": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"set_owner": "kube",
"state": "enabled"
},
},
"KDUMP": {
"config": {
"enabled": "false",
"num_dumps": "3",
"memory": "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M"
}
},
"AAA": {
"authentication": {
"login": "local",
"debug": "True",
}
},
},
"expected_config_db": {
"DEVICE_METADATA": {
"localhost": {
"hostname": "local",
}
},
"FEATURE": {
"dhcp_relay": {
"auto_restart": "enabled",
"has_global_scope": "True",
"has_per_asic_scope": "False",
"has_timer": "False",
"high_mem_alert": "disabled",
"set_owner": "kube",
"state": "enabled"
},
},
"AAA": {
"authentication": {
"login": "local",
"debug": "True",
}
},
},
"expected_subprocess_calls": [
call("service aaastatsd start", shell=True),
],
},
],
]