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:
parent
990b1127a7
commit
ec9101f9c5
@ -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
13
rules/radius.dep
Normal 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
24
rules/radius.mk
Normal 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)
|
||||
|
2
slave.mk
2
slave.mk
@ -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
23
src/radius/nss/Makefile
Normal 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)
|
5
src/radius/nss/debian/README.Debian
Normal file
5
src/radius/nss/debian/README.Debian
Normal 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
|
6
src/radius/nss/debian/changelog
Normal file
6
src/radius/nss/debian/changelog
Normal 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
|
1
src/radius/nss/debian/compat
Normal file
1
src/radius/nss/debian/compat
Normal file
@ -0,0 +1 @@
|
||||
9
|
17
src/radius/nss/debian/control
Normal file
17
src/radius/nss/debian/control
Normal 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.
|
21
src/radius/nss/debian/copyright
Normal file
21
src/radius/nss/debian/copyright
Normal 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
15
src/radius/nss/debian/rules
Executable 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
|
2
src/radius/nss/debian/watch
Normal file
2
src/radius/nss/debian/watch
Normal file
@ -0,0 +1,2 @@
|
||||
# You must remove unused comment lines for the released package.
|
||||
version=3
|
6
src/radius/nss/libnss-radius/.gitignore
vendored
Normal file
6
src/radius/nss/libnss-radius/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
cache_radius
|
||||
libnss_radius.so.2
|
||||
test_cache_radius
|
||||
test_nss_radius
|
||||
debian
|
||||
patches
|
14
src/radius/nss/libnss-radius/LICENSE
Normal file
14
src/radius/nss/libnss-radius/LICENSE
Normal 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.
|
59
src/radius/nss/libnss-radius/Makefile
Normal file
59
src/radius/nss/libnss-radius/Makefile
Normal 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
|
||||
|
198
src/radius/nss/libnss-radius/cache_radius.c
Normal file
198
src/radius/nss/libnss-radius/cache_radius.c
Normal 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));
|
||||
}
|
4
src/radius/nss/libnss-radius/libnss_radius_vs.txt
Normal file
4
src/radius/nss/libnss-radius/libnss_radius_vs.txt
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
global: _nss_radius_getpwnam_r;
|
||||
local: *;
|
||||
};
|
84
src/radius/nss/libnss-radius/nss_radius.c
Normal file
84
src/radius/nss/libnss-radius/nss_radius.c
Normal 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;
|
||||
}
|
||||
|
780
src/radius/nss/libnss-radius/nss_radius_common.c
Normal file
780
src/radius/nss/libnss-radius/nss_radius_common.c
Normal 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(®ex, conf->unconfirmed_regexp,
|
||||
REG_EXTENDED|REG_NOSUB))) {
|
||||
|
||||
errbuf[0] = 0;
|
||||
regerror(reg_ret, ®ex, errbuf, sizeof(errbuf));
|
||||
syslog( LOG_ERR, "%s: %s: regcomp() failed: %s", conf->prog,
|
||||
conf->unconfirmed_regexp, errbuf);
|
||||
|
||||
} else {
|
||||
|
||||
re = ®ex;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
132
src/radius/nss/libnss-radius/nss_radius_common.h
Normal file
132
src/radius/nss/libnss-radius/nss_radius_common.h
Normal 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);
|
||||
|
19
src/radius/nss/libnss-radius/rbash_restriction.sh
Normal file
19
src/radius/nss/libnss-radius/rbash_restriction.sh
Normal 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
|
62
src/radius/nss/libnss-radius/test_nss_radius.c
Normal file
62
src/radius/nss/libnss-radius/test_nss_radius.c
Normal 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
0
src/radius/nss/patches/.gitignore
vendored
Normal file
1
src/radius/pam/.gitignore
vendored
Normal file
1
src/radius/pam/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
pam_radius
|
43
src/radius/pam/Makefile
Normal file
43
src/radius/pam/Makefile
Normal 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)
|
5
src/radius/pam/debian/README.Debian
Normal file
5
src/radius/pam/debian/README.Debian
Normal 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.
|
5
src/radius/pam/debian/changelog
Normal file
5
src/radius/pam/debian/changelog
Normal 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
|
1
src/radius/pam/debian/compat
Normal file
1
src/radius/pam/debian/compat
Normal file
@ -0,0 +1 @@
|
||||
10
|
16
src/radius/pam/debian/control
Normal file
16
src/radius/pam/debian/control
Normal 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
|
284
src/radius/pam/debian/copyright
Normal file
284
src/radius/pam/debian/copyright
Normal 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
|
36
src/radius/pam/debian/index.html
Normal file
36
src/radius/pam/debian/index.html
Normal 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>
|
64
src/radius/pam/debian/pam_example
Normal file
64
src/radius/pam/debian/pam_example
Normal 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
116
src/radius/pam/debian/rules
Executable 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
2
src/radius/pam/freeradius/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
freeradius-1.1.4
|
||||
freeradius-server
|
113
src/radius/pam/freeradius/patches/freeradius_2007-04-06.patch
Normal file
113
src/radius/pam/freeradius/patches/freeradius_2007-04-06.patch
Normal 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
|
1443
src/radius/pam/freeradius/patches/freeradius_configure.patch
Normal file
1443
src/radius/pam/freeradius/patches/freeradius_configure.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
133
src/radius/pam/patches/0001-chap-support.patch
Normal file
133
src/radius/pam/patches/0001-chap-support.patch
Normal 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 */
|
4631
src/radius/pam/patches/0002-peap-mschapv2-support.patch
Normal file
4631
src/radius/pam/patches/0002-peap-mschapv2-support.patch
Normal file
File diff suppressed because it is too large
Load Diff
943
src/radius/pam/patches/0003-nas-ip-address-config.patch
Normal file
943
src/radius/pam/patches/0003-nas-ip-address-config.patch
Normal 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(¤t_time);
|
||||
+ ctime_buf[0] = 0;
|
||||
+ ctime_r(¤t_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
|
||||
|
||||
######################################################################
|
3
src/radius/pam/patches/series
Normal file
3
src/radius/pam/patches/series
Normal file
@ -0,0 +1,3 @@
|
||||
0001-chap-support.patch
|
||||
0002-peap-mschapv2-support.patch
|
||||
0003-nas-ip-address-config.patch
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 %}
|
||||
|
58
src/sonic-host-services-data/templates/radius_nss.conf.j2
Normal file
58
src/sonic-host-services-data/templates/radius_nss.conf.j2
Normal 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.
|
||||
#
|
5
src/sonic-host-services/.gitignore
vendored
5
src/sonic-host-services/.gitignore
vendored
@ -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/*
|
||||
|
@ -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
|
||||
|
222
src/sonic-host-services/scripts/aaastatsd
Executable file
222
src/sonic-host-services/scripts/aaastatsd
Executable 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()
|
||||
|
@ -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))
|
||||
|
@ -16,6 +16,7 @@ setup(
|
||||
scripts = [
|
||||
'scripts/caclmgrd',
|
||||
'scripts/hostcfgd',
|
||||
'scripts/aaastatsd',
|
||||
'scripts/procdockerstatsd',
|
||||
'scripts/determine-reboot-cause',
|
||||
'scripts/process-reboot-cause',
|
||||
|
102
src/sonic-host-services/tests/hostcfgd/hostcfgd_radius_test.py
Normal file
102
src/sonic-host-services/tests/hostcfgd/hostcfgd_radius_test.py
Normal 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)
|
4
src/sonic-host-services/tests/hostcfgd/output/.gitignore
vendored
Normal file
4
src/sonic-host-services/tests/hostcfgd/output/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore all test generated files
|
||||
*
|
||||
# But keep this file
|
||||
!.gitignore
|
@ -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)
|
116
src/sonic-host-services/tests/hostcfgd/sample_output/LOCAL/login
Normal file
116
src/sonic-host-services/tests/hostcfgd/sample_output/LOCAL/login
Normal 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
|
@ -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
|
@ -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.
|
||||
#
|
@ -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
|
@ -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
|
@ -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
|
@ -0,0 +1,2 @@
|
||||
# server[:port] shared_secret timeout(s) source_ip vrf
|
||||
[10.10.10.1]:1645 pass1 1
|
@ -0,0 +1,2 @@
|
||||
# server[:port] shared_secret timeout(s) source_ip vrf
|
||||
[10.10.10.2]:1645 pass2 2
|
@ -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)
|
@ -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
|
@ -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
|
@ -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.
|
||||
#
|
@ -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
|
@ -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
|
@ -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
|
181
src/sonic-host-services/tests/hostcfgd/test_radius_vectors.py
Normal file
181
src/sonic-host-services/tests/hostcfgd/test_radius_vectors.py
Normal 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),
|
||||
],
|
||||
},
|
||||
],
|
||||
]
|
Loading…
Reference in New Issue
Block a user