From 8ea8340ce7ae776134fdaf934ac4a399b1e6a37e Mon Sep 17 00:00:00 2001 From: Arun Barboza Date: Mon, 21 Oct 2019 19:11:31 -0700 Subject: [PATCH] [JIRA SONIC-10898] RADIUS Management User Authentication Feature (1) * First Cut for RADIUS Management User Authentication Feature. * PAP/CHAP Authentication. * NSS Module. * Click Cli Change-Id: Ie079bb7a19f8626ed0470abf3379084b6f3342e2 --- .../build_templates/sonic_debian_extension.j2 | 10 + .../hostcfgd/common-auth-sonic.j2 | 36 ++ files/image_config/hostcfgd/hostcfgd | 113 +++- .../hostcfgd/pam_radius_auth.conf.j2 | 3 + .../image_config/hostcfgd/radius_nss.conf.j2 | 37 ++ rules/radius.mk | 24 + slave.mk | 2 + src/radius/nss/Makefile | 23 + src/radius/nss/debian/README.Debian | 8 + src/radius/nss/debian/changelog | 6 + src/radius/nss/debian/compat | 1 + src/radius/nss/debian/control | 17 + src/radius/nss/debian/copyright | 21 + src/radius/nss/debian/files | 2 + src/radius/nss/debian/rules | 15 + src/radius/nss/debian/watch | 2 + src/radius/nss/libnss-radius/.gitignore | 4 + src/radius/nss/libnss-radius/LICENSE | 14 + src/radius/nss/libnss-radius/Makefile | 51 ++ src/radius/nss/libnss-radius/cache_radius.c | 264 ++++++++++ src/radius/nss/libnss-radius/nss_radius.c | 66 +++ .../nss/libnss-radius/nss_radius_common.c | 483 ++++++++++++++++++ .../nss/libnss-radius/rbash_restriction.sh | 19 + .../nss/libnss-radius/test_nss_radius.c | 67 +++ src/radius/nss/patches/.gitignore | 0 src/radius/pam/Makefile | 26 + src/radius/pam/debian/README.Debian | 5 + src/radius/pam/debian/changelog | 5 + src/radius/pam/debian/compat | 1 + src/radius/pam/debian/control | 16 + src/radius/pam/debian/copyright | 284 ++++++++++ src/radius/pam/debian/files | 2 + src/radius/pam/debian/index.html | 36 ++ src/radius/pam/debian/pam_example | 64 +++ src/radius/pam/debian/rules | 111 ++++ .../pam/patches/0001-chap-support.patch | 133 +++++ src/radius/pam/patches/series | 1 + 37 files changed, 1965 insertions(+), 7 deletions(-) create mode 100644 files/image_config/hostcfgd/pam_radius_auth.conf.j2 create mode 100644 files/image_config/hostcfgd/radius_nss.conf.j2 create mode 100644 rules/radius.mk create mode 100644 src/radius/nss/Makefile create mode 100644 src/radius/nss/debian/README.Debian create mode 100644 src/radius/nss/debian/changelog create mode 100644 src/radius/nss/debian/compat create mode 100644 src/radius/nss/debian/control create mode 100644 src/radius/nss/debian/copyright create mode 100644 src/radius/nss/debian/files create mode 100755 src/radius/nss/debian/rules create mode 100644 src/radius/nss/debian/watch create mode 100644 src/radius/nss/libnss-radius/.gitignore create mode 100644 src/radius/nss/libnss-radius/LICENSE create mode 100644 src/radius/nss/libnss-radius/Makefile create mode 100644 src/radius/nss/libnss-radius/cache_radius.c create mode 100644 src/radius/nss/libnss-radius/nss_radius.c create mode 100644 src/radius/nss/libnss-radius/nss_radius_common.c create mode 100644 src/radius/nss/libnss-radius/rbash_restriction.sh create mode 100644 src/radius/nss/libnss-radius/test_nss_radius.c create mode 100644 src/radius/nss/patches/.gitignore create mode 100644 src/radius/pam/Makefile create mode 100644 src/radius/pam/debian/README.Debian create mode 100644 src/radius/pam/debian/changelog create mode 100644 src/radius/pam/debian/compat create mode 100644 src/radius/pam/debian/control create mode 100644 src/radius/pam/debian/copyright create mode 100644 src/radius/pam/debian/files create mode 100644 src/radius/pam/debian/index.html create mode 100644 src/radius/pam/debian/pam_example create mode 100755 src/radius/pam/debian/rules create mode 100644 src/radius/pam/patches/0001-chap-support.patch create mode 100644 src/radius/pam/patches/series diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 4018e66e0854..c4d7004aff0a 100755 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -170,6 +170,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 the HWDIAG package sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/hwdiag*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f diff --git a/files/image_config/hostcfgd/common-auth-sonic.j2 b/files/image_config/hostcfgd/common-auth-sonic.j2 index 564457552382..a989c149db0d 100644 --- a/files/image_config/hostcfgd/common-auth-sonic.j2 +++ b/files/image_config/hostcfgd/common-auth-sonic.j2 @@ -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 }} 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 }} 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) + 1 }} 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 }} try_first_pass +{% endfor %} +auth requisite pam_deny.so +# Cache MPL(Privilege) +auth [success=1 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 diff --git a/files/image_config/hostcfgd/hostcfgd b/files/image_config/hostcfgd/hostcfgd index 359f4dfda1fb..dca9940cd745 100755 --- a/files/image_config/hostcfgd/hostcfgd +++ b/files/image_config/hostcfgd/hostcfgd @@ -15,6 +15,9 @@ 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" # TACACS+ @@ -22,6 +25,14 @@ TACPLUS_SERVER_PASSKEY_DEFAULT = "" TACPLUS_SERVER_TIMEOUT_DEFAULT = "5" TACPLUS_SERVER_AUTH_TYPE_DEFAULT = "pap" +# 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_valid_hostname(name): if hostname[-1] == ".": @@ -60,19 +71,32 @@ 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 = { + '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 # 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): @@ -101,6 +125,22 @@ class AaaCfg(object): if modify_conf: self.modify_conf_file() + def radius_global_update(self, key, data, modify_conf=True): + if key == 'global': + self.radius_global = data + 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 modify_conf_file(self): auth = self.auth_default.copy() auth.update(self.auth) @@ -116,11 +156,27 @@ class AaaCfg(object): servers_conf.append(server) servers_conf = sorted(servers_conf, key=lambda t: t['priority'], reverse=True) + radius_global = self.radius_global_default.copy() + radius_global.update(self.radius_global) + + 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]) + radsrvs_conf.append(server) + radsrvs_conf = sorted(radsrvs_conf, key=lambda t: 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, servers=servers_conf) + if 'radius' in auth['login']: + pam_conf = template.render(auth=auth, servers=radsrvs_conf) + else: + pam_conf = template.render(auth=auth, servers=servers_conf) + with open(PAM_AUTH_CONF, 'w') as f: f.write(pam_conf) @@ -132,13 +188,16 @@ class AaaCfg(object): os.system("sed -i -e '/^@include/s/common-auth-sonic$/common-auth/' /etc/pam.d/sshd") os.system("sed -i -e '/^@include/s/common-auth-sonic$/common-auth/' /etc/pam.d/login") - # Add tacplus in nsswitch.conf if TACACS+ enable + # Add tacplus/radius in nsswitch.conf if TACACS+/RADIUS enable if 'tacacs+' in auth['login']: if os.path.isfile(NSS_CONF): - os.system("sed -i -e '/tacplus/b' -e '/^passwd/s/compat/tacplus &/' /etc/nsswitch.conf") + os.system("sed -i -e '/^passwd/s/ radius//' -e '/tacplus/b' -e '/^passwd/s/compat/tacplus &/' /etc/nsswitch.conf") + elif 'radius' in auth['login']: + if os.path.isfile(NSS_CONF): + os.system("sed -i -e '/^passwd/s/tacplus //' -e '/radius/b' -e '/^passwd/s/compat/& radius/' /etc/nsswitch.conf") else: if os.path.isfile(NSS_CONF): - os.system("sed -i -e '/^passwd/s/tacplus //' /etc/nsswitch.conf") + os.system("sed -i -e '/^passwd/s/tacplus //' -e '/^passwd/s/ radius//' /etc/nsswitch.conf") # Set tacacs+ server in nss-tacplus conf template_file = os.path.abspath(NSS_TACPLUS_CONF_TEMPLATE) @@ -147,6 +206,28 @@ 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, 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 self.radius_servers: + for addr in self.radius_servers: + srv = radius_global.copy() + srv['ip'] = addr + srv.update(self.radius_servers[addr]) + pam_radius_auth_file = RADIUS_PAM_AUTH_CONF_DIR + addr + ":" + 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) + + with open(pam_radius_auth_file, 'w') as f: + f.write(pam_radius_auth_conf) + + class HostConfigDaemon: def __init__(self): @@ -156,8 +237,10 @@ 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') + radius_global = self.config_db.get_table('RADIUS') + radius_server = self.config_db.get_table('RADIUS_SERVER') self.aaacfg = AaaCfg() - self.aaacfg.load(aaa, tacacs_global, tacacs_server) + self.aaacfg.load(aaa, tacacs_global, tacacs_server, radius_global, radius_server) self.hostname_cache="" def aaa_handler(self, key, data): @@ -177,6 +260,20 @@ 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 log_data.has_key('passkey'): + 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 log_data.has_key('passkey'): + log_data['passkey'] = obfuscate(log_data['passkey']) + syslog.syslog(syslog.LOG_INFO, 'value of {} changed to {}'.format(key, log_data)) + def hostname_handler(self, key, data): if key != "localhost": return @@ -219,6 +316,8 @@ 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('DEVICE_METADATA', lambda table, key, data: self.hostname_handler(key, data)) self.config_db.listen() diff --git a/files/image_config/hostcfgd/pam_radius_auth.conf.j2 b/files/image_config/hostcfgd/pam_radius_auth.conf.j2 new file mode 100644 index 000000000000..0889b07772c0 --- /dev/null +++ b/files/image_config/hostcfgd/pam_radius_auth.conf.j2 @@ -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 %} + diff --git a/files/image_config/hostcfgd/radius_nss.conf.j2 b/files/image_config/hostcfgd/radius_nss.conf.j2 new file mode 100644 index 000000000000..9c3b3bde0aa8 --- /dev/null +++ b/files/image_config/hostcfgd/radius_nss.conf.j2 @@ -0,0 +1,37 @@ +# 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;uid=1000;gid=1000;group=sudo,docker;dir=/home/admin;shell=/bin/bash +# user_priv=1;pw_info=remote_user;uid=65534;gid=65534;group=users;dir=/var/tmp;shell=/bin/bash + +# Eg: +# First need to create netops, operator using: +# useradd netops -G users -u 2007 -g 100 -c "netops" -m -s /bin/bash +# useradd operator -G users -u 2001 -g 100 -c "operator" -m -s /bin/rbash +# +# Then uncomment the lines below. +# +# user_priv=15;pw_info=remote_user_su;uid=1000;gid=1000;group=sudo,docker;dir=/home/admin;shell=/bin/bash +# user_priv=7;pw_info=netops;uid=2007;gid=100;group=users;dir=/home/netops;shell=/bin/bash +# user_priv=1;pw_info=operator;uid=2001;gid=100;group=users;dir=/home/operator;shell=/bin/rbash + +# many_to_one: +# y: Map TACACS+ users to one local user per privilege. +# n: Create local user account on first successful authentication. +# a: Anonymous: Return least privileged(user_priv=1) user info for unknown. +# Default: n +# + +# Eg: +# many_to_one=y +# diff --git a/rules/radius.mk b/rules/radius.mk new file mode 100644 index 000000000000..643acfd497b2 --- /dev/null +++ b/rules/radius.mk @@ -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) + diff --git a/slave.mk b/slave.mk index c1a1457f05d6..8e06ab681d45 100644 --- a/slave.mk +++ b/slave.mk @@ -880,6 +880,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ $(IFUPDOWN2) \ $(HWDIAG) \ $(NTP) \ + $(LIBPAM_RADIUS) \ + $(LIBNSS_RADIUS) \ $(LIBPAM_TACPLUS) \ $(LIBNSS_TACPLUS)) \ $$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) \ diff --git a/src/radius/nss/Makefile b/src/radius/nss/Makefile new file mode 100644 index 000000000000..6559ae585edb --- /dev/null +++ b/src/radius/nss/Makefile @@ -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) diff --git a/src/radius/nss/debian/README.Debian b/src/radius/nss/debian/README.Debian new file mode 100644 index 000000000000..46141eb04d71 --- /dev/null +++ b/src/radius/nss/debian/README.Debian @@ -0,0 +1,8 @@ +libnss-radius for Debian + +Please edit this to provide information specific to +this libnss-radius Debian package. + + (Automatically generated by debmake Version 4.2.2) + + -- Arun Barboza <29963827+a-barboza@users.noreply.github.com> Tue, 24 Sep 2019 00:20:55 +0000 diff --git a/src/radius/nss/debian/changelog b/src/radius/nss/debian/changelog new file mode 100644 index 000000000000..ee1cc9150468 --- /dev/null +++ b/src/radius/nss/debian/changelog @@ -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 diff --git a/src/radius/nss/debian/compat b/src/radius/nss/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/src/radius/nss/debian/compat @@ -0,0 +1 @@ +9 diff --git a/src/radius/nss/debian/control b/src/radius/nss/debian/control new file mode 100644 index 000000000000..a7903b0571e2 --- /dev/null +++ b/src/radius/nss/debian/control @@ -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. diff --git a/src/radius/nss/debian/copyright b/src/radius/nss/debian/copyright new file mode 100644 index 000000000000..f56de29d49bc --- /dev/null +++ b/src/radius/nss/debian/copyright @@ -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. diff --git a/src/radius/nss/debian/files b/src/radius/nss/debian/files new file mode 100644 index 000000000000..7be8e46255c6 --- /dev/null +++ b/src/radius/nss/debian/files @@ -0,0 +1,2 @@ +libnss-radius_1.0.1-1_amd64.buildinfo libs optional +libnss-radius_1.0.1-1_amd64.deb libs optional diff --git a/src/radius/nss/debian/rules b/src/radius/nss/debian/rules new file mode 100755 index 000000000000..dca10d171750 --- /dev/null +++ b/src/radius/nss/debian/rules @@ -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 diff --git a/src/radius/nss/debian/watch b/src/radius/nss/debian/watch new file mode 100644 index 000000000000..76575dc53be5 --- /dev/null +++ b/src/radius/nss/debian/watch @@ -0,0 +1,2 @@ +# You must remove unused comment lines for the released package. +version=3 diff --git a/src/radius/nss/libnss-radius/.gitignore b/src/radius/nss/libnss-radius/.gitignore new file mode 100644 index 000000000000..b199d516f7f3 --- /dev/null +++ b/src/radius/nss/libnss-radius/.gitignore @@ -0,0 +1,4 @@ +cache_radius +libnss_radius.so.2 +test_cache_radius +test_nss_radius diff --git a/src/radius/nss/libnss-radius/LICENSE b/src/radius/nss/libnss-radius/LICENSE new file mode 100644 index 000000000000..00d5b1b9fabe --- /dev/null +++ b/src/radius/nss/libnss-radius/LICENSE @@ -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. diff --git a/src/radius/nss/libnss-radius/Makefile b/src/radius/nss/libnss-radius/Makefile new file mode 100644 index 000000000000..f46b084dca44 --- /dev/null +++ b/src/radius/nss/libnss-radius/Makefile @@ -0,0 +1,51 @@ +####################################################################### +# +# Copyright 2019 Broadcom. All rights reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +####################################################################### + +# +# Makefile for libnss-radius +# + + +# 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: libnss_radius.so.2 cache_radius rbash_restriction.sh + +libnss_radius.so.2: nss_radius.c nss_radius_common.c + $(CC) $(CFLAGS) $(LDFLAGS) -fPIC -Wall -shared -o libnss_radius.so.2 \ + -Wl,-soname,libnss_radius.so.2 nss_radius.c + +cache_radius: cache_radius.c nss_radius_common.c + $(CC) $(CFLAGS) $(LDFLAGS) -o cache_radius cache_radius.c + +clean: + -rm -f libnss_radius.so.2 cache_radius + -rm -f test_nss_radius test_cache_radius + +install: libnss_radius.so.2 cache_radius rbash_restriction.sh + 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/ + install -m 0644 -D rbash_restriction.sh $(DESTDIR)$(prefix)/etc/profile.d/rbash_restriction.sh + +distclean: clean + +uninstall: + -rm -f $(DESTDIR)$(moduledir)/libnss_radius.so.2 + -rm -f $(DESTDIR)$(prefix)/usr/sbin/cache_radius + -rm -f $(DESTDIR)$(prefix)/etc/profile.d/rbash_restriction.sh + +test: nss_radius.c test_nss_radius.c + $(CC) $(CFLAGS) $(LDFLAGS) -g -o test_nss_radius test_nss_radius.c + $(CC) $(CFLAGS) $(LDFLAGS) -g -DTEST_RADIUS_NSS -o test_cache_radius cache_radius.c + + +.PHONY: all install clean distclean uninstall test + diff --git a/src/radius/nss/libnss-radius/cache_radius.c b/src/radius/nss/libnss-radius/cache_radius.c new file mode 100644 index 000000000000..4bc01940eeb0 --- /dev/null +++ b/src/radius/nss/libnss-radius/cache_radius.c @@ -0,0 +1,264 @@ +/* +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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USERADD "/usr/sbin/useradd" +#define ETC_NSSWITCHCONF "/etc/nsswitch.conf" + +#if defined(TEST_RADIUS_NSS) +#define syslog(priority,format, ...) printf("priority:%d " format "\n", priority, __VA_ARGS__) + +#undef USERADD +#define USERADD "/bin/echo" + +#undef ETC_NSSWITCHCONF +#define ETC_NSSWITCHCONF "nsswitch.conf" + +#endif + +#include "nss_radius_common.c" + +#define BUFLEN 4096 + +static int local_files_user_status(char * prog, const char * nam) { + struct passwd pw, * pwd = NULL; + FILE * fp = NULL; + int status = 1; + char buf[BUFLEN]; + + if ((fp = fopen(ETC_PASSWD, "r")) == NULL) { + syslog(LOG_WARNING, "%s: fopen(\"/etc/passwd\") failed\n", prog); + status = 2; + goto lfus_exit; + + } + + while((status = fgetpwent_r(fp, &pw, buf, sizeof(buf), &pwd)) == 0) { + if (pwd && pwd->pw_name && (strcmp(pwd->pw_name, nam) == 0)) { + status = 0; + break; + } + } + +lfus_exit: + + 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; + int status = 0; + 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); + status = 1; + goto radius_update_cache_exit; + } + + /* 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); + status = 2; + goto radius_update_cache_exit; + } + + /* 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); + status = 2; + goto radius_update_cache_exit; + } + + syslog(LOG_INFO, "%s: MPL %d updated for user %s", prog, mpl, nam); + +radius_update_cache_exit: + + /* Umask restore. + */ + umask(mask); + + if (fp) + fclose(fp); + + + return status; +} + + +static int invoke_popen(char * prog, char * cmd) { + FILE * fp; + int status = 0; + + if (debug) + syslog(LOG_DEBUG, "%s:%s", prog, cmd); + + if (((fp = popen(cmd, "r")) == NULL) || (pclose(fp) == -1)) { + syslog(LOG_ERR, "%s: %s: popen()/pclose() failed %p, errno=%d", + prog, cmd, fp, errno); + status = 1; + } + + return status; +} + + + +int main(int ac, char * av[]) { + + int mpl = 1; + int status = 0; + int my_errno = 0; + int written = 0; + int parsed = 0; + char * user = NULL, * privilege; + RADIUS_NSS_MPL radius_nss_mpl[RADIUS_MAX_MPL], * rnm; + char file_buf[RADIUS_MAX_NSS_CONF_SZ]; + char useradd[4096]; + int ncfd = -1; + + /* Read environment PAM_USER. + */ + + if ( ((user = getenv("PAM_USER")) == NULL) + || (user[0] == 0)) { + status = 1; + syslog(LOG_WARNING, + "%s: Missing or bad PAM_USER in environment:\"%s\"\n", + av[0], user ? user : ""); + goto main_exit; + } + + /* 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 mpl %d detected\n", av[0], mpl); + mpl = 1; + } + } else { + syslog(LOG_INFO, "%s: Missing or bad Privilege in environment:\"%s\"\n", + av[0], privilege ? privilege : ""); + } + + /* If the user is present locally. + */ + if (local_files_user_status(av[0], user) == 0) { + goto main_exit; + } + + parse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, + file_buf, RADIUS_MAX_NSS_CONF_SZ, &my_errno, &ncfd); + parsed = 1; + + if (many_to_one) { + goto main_exit; + } + + /* Create the user. + */ + syslog(LOG_INFO, "%s: Adding user %s", av[0], user); + + rnm = &(radius_nss_mpl[mpl-1]); + + if (trace) + dump_rnm(mpl, rnm); + + written = snprintf(useradd, sizeof(useradd), + "%s -g %d -G %s -c \"%s\" -d /home/%s -m -s %s \"%s\"", + USERADD, rnm->gid, rnm->group, rnm->gecos, user, + rnm->shell, user); + + if (written >= sizeof(useradd)) { + status = 2; + syslog(LOG_WARNING, + "%s: truncated useradd cmd. Skipping:\"%s\"\n", + av[0], useradd); + goto main_exit; + } + +#if NEED_BLOCK_RADIUS_NSS + if ( allow_anonymous + && (invoke_popen(av[0], + "/bin/sed -i -e '/^passwd/s/ radius//' " ETC_NSSWITCHCONF) != 0)) { + status = 3; + goto main_exit; + } +#endif + + invoke_popen(av[0], useradd); + +#if NEED_BLOCK_RADIUS_NSS + if ( allow_anonymous + && (invoke_popen(av[0], + "/bin/sed -i -e '/^passwd/s/compat$/& radius/' " ETC_NSSWITCHCONF) + != 0)) { + status = 4; + goto main_exit; + } +#endif + +main_exit: + + if (status == 0) { + radius_update_cache(av[0], user, mpl); + } + + if (parsed) + unparse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, &my_errno, &ncfd); + + exit(status); +} diff --git a/src/radius/nss/libnss-radius/nss_radius.c b/src/radius/nss/libnss-radius/nss_radius.c new file mode 100644 index 000000000000..25b65e4c33d7 --- /dev/null +++ b/src/radius/nss/libnss-radius/nss_radius.c @@ -0,0 +1,66 @@ +/* +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.c" + +/* + * 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; + RADIUS_NSS_MPL radius_nss_mpl[RADIUS_MAX_MPL]; + char file_buf[RADIUS_MAX_NSS_CONF_SZ]; + + /* Ignore filename completion. + */ + if (!nam || !strcmp(nam, "*") || !pwd || !buf || (buflen == 0)) + return NSS_STATUS_NOTFOUND; + + if (radius_lookup_cache(nam, &mpl) == 0) { + + status = NSS_STATUS_SUCCESS; + if (debug) + syslog( LOG_DEBUG, "nam: %s", nam); + + parse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, + file_buf, RADIUS_MAX_NSS_CONF_SZ, errnop, NULL); + + /* Based on the mpl, fill the pwd + */ + radius_fill_pw(nam, pwd, buf, buflen, &(radius_nss_mpl[mpl-1]), errnop); + + unparse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, errnop, NULL); + + } else { + + parse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, + file_buf, RADIUS_MAX_NSS_CONF_SZ, errnop, NULL); + + if (allow_anonymous) { + + /* Could be an sshd doing a getpwnam() before pam_authenticate(). + * Give the least privileged user info. + */ + + status = NSS_STATUS_SUCCESS; + init_rnm(radius_nss_mpl, RADIUS_MAX_MPL); + radius_fill_pw(nam, pwd, buf, buflen, &(radius_nss_mpl[0]), errnop); + } + + unparse_nss_config(radius_nss_mpl, RADIUS_MAX_MPL, errnop, NULL); + } + + + return status; +} + diff --git a/src/radius/nss/libnss-radius/nss_radius_common.c b/src/radius/nss/libnss-radius/nss_radius_common.c new file mode 100644 index 000000000000..483e7599ab21 --- /dev/null +++ b/src/radius/nss/libnss-radius/nss_radius_common.c @@ -0,0 +1,483 @@ +/* +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug = 0; +static int trace = 0; +static int many_to_one = 0; +static int allow_anonymous = 0; + + +#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/run/radius/user/" +#define RADIUS_CACHE_DIR "/var/run/radius/" +#define RADIUS_ATTR_MPL "Management-Privilege-Level" + +#define ETC_PASSWD "/etc/passwd" + +#if defined(TEST_RADIUS_NSS) +#undef RADIUS_NSS_CONF +#define RADIUS_NSS_CONF "radius_nss.conf" +#undef RADIUS_ATTRIBUTE_CACHE_DIR +#define RADIUS_ATTRIBUTE_CACHE_DIR "user" +#undef ETC_PASSWD +#define ETC_PASSWD "passwd" +#endif + +typedef struct _radius_nss_mpl { + uid_t uid; + gid_t gid; + char * group; + char * gecos; + char * dir; + char * shell; +} RADIUS_NSS_MPL; + + +static void dump_rnm(int mpl, RADIUS_NSS_MPL * rnm) { + + syslog( LOG_DEBUG, "dump_rnm: " + "mpl %d uid %d gid %d group \"%s\" gecos \"%s\" dir \"%s\" shell \"%s\"", + mpl, rnm->uid, rnm->gid, rnm->group, rnm->gecos, rnm->dir, rnm->shell); +} + +static char * parse_line(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 (trace) + syslog(LOG_DEBUG, "parse_line: \"%s\"\n", line ? line : ""); + + return line; +} + +static char * parse_token(char ** pbufpos, int * premain) { + char * token = NULL; + + for( token = *pbufpos; + (*premain) && (**pbufpos) && ((**pbufpos) != ';') ; + (*pbufpos)++, (*premain)--) + ; /* Empty Body */ + + if (*premain) { + *((*pbufpos)++) = 0; + (*premain)--; + } + + if (trace) + syslog(LOG_DEBUG, "parse_token: \"%s\"\n", token ? token : ""); + + return token; +} + +static int parse_user(char * line, RADIUS_NSS_MPL * rnm, int max_mpl) { + RADIUS_NSS_MPL parse_rnm; + char * token, * bufpos; + int mpl; + int ret = 0, remain; + + bufpos = line; + remain = strlen(line); + memset((char *) &parse_rnm, 0, sizeof(parse_rnm)); + + while (remain && (token = parse_token(&bufpos, &remain))) { + if (strncmp(token, "user_priv=", 10) == 0) { + mpl = atoi(&token[10]); + } else if (strncmp(token, "group=", 6) == 0) { + parse_rnm.group = &(token[6]); + } else if (strncmp(token, "pw_info=", 8) == 0) { + parse_rnm.gecos = &(token[8]); + } else if (strncmp(token, "uid=", 4) == 0) { + parse_rnm.uid = atol(&(token[4])); + } else if (strncmp(token, "gid=", 4) == 0) { + parse_rnm.gid = atol(&(token[4])); + } else if (strncmp(token, "dir=", 4) == 0) { + parse_rnm.dir = &(token[4]); + } else if (strncmp(token, "shell=", 6) == 0) { + parse_rnm.shell = &(token[6]); + } + } + + if (trace) + dump_rnm(mpl, &parse_rnm); + + if ( ((RADIUS_MIN_MPL > mpl) || (mpl > RADIUS_MAX_MPL)) + || (parse_rnm.uid == 0) + || (parse_rnm.gid == 0) + || (parse_rnm.group == NULL) + || (parse_rnm.gecos == NULL) + || (parse_rnm.dir == NULL) + || (parse_rnm.shell == NULL)) { + + syslog( LOG_WARNING, "%s: Bad user_priv line \"%s\"", RADIUS_NSS_CONF, + line); + ret = 1; + } else { + rnm[mpl-1] = parse_rnm; + } + + return ret; +} + +static void init_rnm(RADIUS_NSS_MPL * rnm, int max_mpl) { + + /* Set rnm[0,max_mpl-1]. + */ + + memset((char *) rnm, 0, sizeof(*rnm) * max_mpl); + rnm[0].uid = 65534; + rnm[0].gid = 65534; + rnm[0].group = "users"; + rnm[0].gecos = "remote_user"; + rnm[0].dir = "/var/tmp"; + rnm[0].shell = "/bin/rbash"; + rnm[max_mpl-1].uid = 1000; + rnm[max_mpl-1].gid = 1000; + rnm[max_mpl-1].group = "sudo,docker"; + rnm[max_mpl-1].gecos = "remote_user_su"; + rnm[max_mpl-1].dir = "/home/admin"; + rnm[max_mpl-1].shell = "/bin/bash"; + +} + +static int parse_nss_config(RADIUS_NSS_MPL * rnm, int max_mpl, + 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; + + init_rnm(rnm, max_mpl); + + debug = trace = 0; + + many_to_one = allow_anonymous = 0; + + /* 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", RADIUS_NSS_CONF, 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", + RADIUS_NSS_CONF, 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", RADIUS_NSS_CONF, + 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(file_buf, &scanpos, &remain)) != NULL) { + + if (strncmp(line, "debug=", 6) == 0) { + + if (strncmp(&(line[6]), "on", 2) == 0) { + + /* Handle "debug". + */ + + debug = 1; + + } else if (strncmp(&(line[6]), "trace", 5) == 0) { + + debug = 1; + trace = 1; + + } + + } else if (strncmp(line, "user_priv=", 10) == 0) { + + /* Handle "user_priv" + */ + + parse_user(line, rnm, max_mpl); + + } else if (strncmp(line, "many_to_one=", 12) == 0) { + + /* Handle "many_to_one" + */ + + if (strncmp(&(line[12]), "y", 1) == 0) { + + many_to_one = 1; + + } else if (strncmp(&(line[12]), "a", 1) == 0) { + + /* Either we've been asked to lock the file, + * (done just before exiting), or we need to + * ensure that we can allow_anonymous. + * An existing exclusive lock on the file, indicates + * that disabling of allow_anonymous is requested + * before useradd is run (otherwise useradd will + * complain about the user being present, and + * not create the user). + */ + if (plockfd || (flock(ncfd, LOCK_EX|LOCK_NB) == 0)) + allow_anonymous = 1; + else + syslog( LOG_DEBUG, "%s: %d: many_to_one=a: is locked out", + RADIUS_NSS_CONF, (int) getpid()); + } + + } else { + + syslog( LOG_WARNING, "%s: Ignorning \"%s\"", RADIUS_NSS_CONF, + line); + } + } + +parse_nss_config_exit: + + if (ncfd != -1) { + /* If the caller requested a lock, do it now. + */ + if (plockfd && (flock(ncfd, LOCK_EX|LOCK_NB) == 0)) { + syslog( LOG_DEBUG, "%s: %d: many_to_one=a: lock success", + RADIUS_NSS_CONF, (int) getpid()); + *plockfd = ncfd; + } else { + if (plockfd) { + syslog( LOG_ERR, "%s: %d: many_to_one=a: lock fail", + RADIUS_NSS_CONF, (int) getpid()); + *plockfd = -1; + } + close(ncfd); /* This should release the lock, if we placed one */ + } + } + + /* Fix up rnm. + */ + for ( i = 1; i < max_mpl - 1; i++) { + if (rnm[i].uid == 0) { + rnm[i] = rnm[i-1]; + } + } + + return ret; +} + +/* Releases any memory. + * Closes any fds. + */ +static int unparse_nss_config(RADIUS_NSS_MPL * rnm, int max_mpl, int * errnop, + int * plockfd) { + + /* If the caller had requested a lock in parse_nss_config(), + * we should now release that lock. + */ + if (plockfd && (*plockfd != -1)) { + if (flock(*plockfd, LOCK_UN|LOCK_NB) == 0) { + syslog( LOG_DEBUG, "%s: %d: many_to_one=a: unlock success", + RADIUS_NSS_CONF, (int) getpid()); + } else { + syslog( LOG_DEBUG, "%s: %d: many_to_one=a: unlock fail", + RADIUS_NSS_CONF, (int) getpid()); + } + close(*plockfd); + *plockfd = -1; + } + return 0; +} + +static int radius_lookup_cache( const char * nam, int * pmpl) { + int rafd = -1; + int i; + char cache_filename[PATH_MAX]; + char file_buf[10]; + struct stat sb; + int ret = 0; + 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.", RADIUS_NSS_CONF, + cache_filename); + ret = 1; + goto radius_lookup_cache_exit; + } + + /* 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_DEBUG, "%s: \"%s\": Absent.", RADIUS_NSS_CONF, + cache_filename); + ret = 1; + goto radius_lookup_cache_exit; + } + + /* 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); + goto radius_lookup_cache_exit; + } + + if ((i = read(rafd, file_buf, sizeof(file_buf))) != sb.st_size) { + syslog( LOG_WARNING, "%s: read %d of %ld. Ignoring", RADIUS_NSS_CONF, + i, sb.st_size); + goto radius_lookup_cache_exit; + } + + file_buf[sb.st_size] = 0; + mpl = atoi(file_buf); + + if (((RADIUS_MIN_MPL <= mpl) && (mpl <= RADIUS_MAX_MPL))) + *pmpl = mpl; + +radius_lookup_cache_exit: + + if (rafd != -1) + close(rafd); + + return ret; +} + + +static int radius_fill_pw( const char * nam, struct passwd * pwd, + char * buffer, size_t buflen, RADIUS_NSS_MPL * rnm, int * errnop) { + + int ret = 0; + size_t namlen, gecoslen, dirlen, shelllen; + + size_t totallen = (namlen = strlen(nam)) + 1 + + (gecoslen = strlen(rnm->gecos)) + 1 + + (dirlen = strlen(rnm->dir)) + 1 + + (shelllen = strlen(rnm->shell)) + 1 + + 1 + 1; + + if (totallen > buflen) { + if (errnop) + *errnop = ERANGE; + ret = 1; + if (debug) + syslog(LOG_DEBUG, "%s: getpwnam_r ERANGE %ld < %ld\n", + nam, buflen, totallen); + goto radius_fill_pw_exit; + } + + memcpy( buffer, nam, namlen + 1); + pwd->pw_name = buffer; + buffer += (namlen + 1); + + memcpy( buffer, "*", 1 + 1); + pwd->pw_passwd = buffer; + buffer += (1 + 1); + + pwd->pw_uid = rnm->uid; + + pwd->pw_gid = rnm->gid; + + memcpy( buffer, rnm->gecos, gecoslen + 1); + pwd->pw_gecos = buffer; + buffer += (gecoslen + 1); + + memcpy( buffer, rnm->dir, dirlen + 1); + pwd->pw_dir = buffer; + buffer += (dirlen + 1); + + memcpy( buffer, rnm->shell, shelllen + 1); + pwd->pw_shell = buffer; + buffer += (shelllen + 1); + +radius_fill_pw_exit: + + return ret; +} + + diff --git a/src/radius/nss/libnss-radius/rbash_restriction.sh b/src/radius/nss/libnss-radius/rbash_restriction.sh new file mode 100644 index 000000000000..50158abd4993 --- /dev/null +++ b/src/radius/nss/libnss-radius/rbash_restriction.sh @@ -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 diff --git a/src/radius/nss/libnss-radius/test_nss_radius.c b/src/radius/nss/libnss-radius/test_nss_radius.c new file mode 100644 index 000000000000..dcf2396b3abe --- /dev/null +++ b/src/radius/nss/libnss-radius/test_nss_radius.c @@ -0,0 +1,67 @@ +/* +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_RADIUS_NSS +#define syslog(priority,format, ...) printf("priority:%d " format "\n", priority, __VA_ARGS__) + +#include "nss_radius.c" + +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: %ld, gid: %ld\n", + pw.pw_name, pw.pw_passwd, pw.pw_uid, pw.pw_gid); + printf("\tnam: %p, passwd: %p, uid: %ld, gid: %ld\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; +} + diff --git a/src/radius/nss/patches/.gitignore b/src/radius/nss/patches/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/radius/pam/Makefile b/src/radius/pam/Makefile new file mode 100644 index 000000000000..9598f63e27da --- /dev/null +++ b/src/radius/pam/Makefile @@ -0,0 +1,26 @@ +.ONESHELL: +SHELL = /bin/bash +.SHELLFLAGS += -e + +MAIN_TARGET = libpam-radius-auth_$(PAM_RADIUS_VERSION)_amd64.deb + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + # 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 + + dpkg-buildpackage -rfakeroot -b -us -uc -nc + popd + + mv $(DERIVED_TARGETS) $* $(DEST)/ + +$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET) diff --git a/src/radius/pam/debian/README.Debian b/src/radius/pam/debian/README.Debian new file mode 100644 index 000000000000..d0bf25b50fcf --- /dev/null +++ b/src/radius/pam/debian/README.Debian @@ -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. diff --git a/src/radius/pam/debian/changelog b/src/radius/pam/debian/changelog new file mode 100644 index 000000000000..0536fb03bda1 --- /dev/null +++ b/src/radius/pam/debian/changelog @@ -0,0 +1,5 @@ +libpam-radius-auth (1.4.1-1) unstable; urgency=low + + * Initial version. + + -- Arun Barboza Tue, 13 Aug 2019 16:46:25 -0700 diff --git a/src/radius/pam/debian/compat b/src/radius/pam/debian/compat new file mode 100644 index 000000000000..f599e28b8ab0 --- /dev/null +++ b/src/radius/pam/debian/compat @@ -0,0 +1 @@ +10 diff --git a/src/radius/pam/debian/control b/src/radius/pam/debian/control new file mode 100644 index 000000000000..f5667970c3fb --- /dev/null +++ b/src/radius/pam/debian/control @@ -0,0 +1,16 @@ +Source: libpam-radius-auth +Maintainer: Debian QA Group +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 diff --git a/src/radius/pam/debian/copyright b/src/radius/pam/debian/copyright new file mode 100644 index 000000000000..5c31e50ff07c --- /dev/null +++ b/src/radius/pam/debian/copyright @@ -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 diff --git a/src/radius/pam/debian/files b/src/radius/pam/debian/files new file mode 100644 index 000000000000..86db272790e9 --- /dev/null +++ b/src/radius/pam/debian/files @@ -0,0 +1,2 @@ +libpam-radius-auth_1.4.1-1_amd64.buildinfo libs extra +libpam-radius-auth_1.4.1-1_amd64.deb libs extra diff --git a/src/radius/pam/debian/index.html b/src/radius/pam/debian/index.html new file mode 100644 index 000000000000..bbd65616d805 --- /dev/null +++ b/src/radius/pam/debian/index.html @@ -0,0 +1,36 @@ + + +<code>pam_radius_auth</CODE>: The PAM RADIUS authentication module + + +

pam_radius_auth: 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 need a RADIUS server to perform the +actual authentication. + +


+

Files included with the module

+ +README Introduction and documentation
+INSTALL Installation instructions
+USAGE Module configuration and usage documentation
+Changelog What's changed
+pam_radius_auth.conf Sample +configuration file for telling the client the location of the RADIUS server.
+Mini Debian HOWTO C source file
+
+ + +


+

Updates

+ +For the latest version and updates, see the main web or ftp site: +

+http://www.freeradius.org/pam_radius_auth/
+ftp://ftp.freeradius.org/pub/radius/pam_radius_auth.tar +


+$Id: index.html,v 1.4 2001/03/30 19:01:56 aland Exp $ + + diff --git a/src/radius/pam/debian/pam_example b/src/radius/pam/debian/pam_example new file mode 100644 index 000000000000..1ab27e861631 --- /dev/null +++ b/src/radius/pam/debian/pam_example @@ -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. + diff --git a/src/radius/pam/debian/rules b/src/radius/pam/debian/rules new file mode 100755 index 000000000000..bf812451731e --- /dev/null +++ b/src/radius/pam/debian/rules @@ -0,0 +1,111 @@ +#!/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 --docdir=/usr/share/doc/libpam-tacplus + + $(MAKE) -e + + 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 /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 pam_radius_auth.conf debian/libpam-radius-auth/etc/pam_radius_auth.conf + 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 + 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 diff --git a/src/radius/pam/patches/0001-chap-support.patch b/src/radius/pam/patches/0001-chap-support.patch new file mode 100644 index 000000000000..91af430ffcab --- /dev/null +++ b/src/radius/pam/patches/0001-chap-support.patch @@ -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 */ diff --git a/src/radius/pam/patches/series b/src/radius/pam/patches/series new file mode 100644 index 000000000000..028cc6245785 --- /dev/null +++ b/src/radius/pam/patches/series @@ -0,0 +1 @@ +0001-chap-support.patch