From 0fa3957d5b6486f01d279c4afcbb96d67316a400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AD=E6=9B=B2?= Date: Wed, 7 Jun 2017 06:31:16 -0700 Subject: [PATCH 1/7] [TACACS+]: Add TACACS+ Feature Summary: * Add pam-tacplus module * Add nss-tacplus module * Add TACACS+ make rules and install script * Add TACACS+ build dependence packages * Add remote_user and remote_user_su * Add AAA configuration file --- .gitmodules | 6 ++++ build_debian.sh | 11 ++++++- .../build_templates/sonic_debian_extension.j2 | 10 +++++++ files/image_config/aaa/aaa.json | 13 +++++++++ rules/tacacs.mk | 29 +++++++++++++++++++ slave.mk | 4 +-- sonic-slave/Dockerfile | 2 ++ src/tacacs/sonic-nss-tacplus | 1 + src/tacacs/sonic-pam-tacplus | 1 + 9 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 files/image_config/aaa/aaa.json create mode 100644 rules/tacacs.mk create mode 160000 src/tacacs/sonic-nss-tacplus create mode 160000 src/tacacs/sonic-pam-tacplus diff --git a/.gitmodules b/.gitmodules index 454019f9ebdd..e1c5cb20cc0f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -66,3 +66,9 @@ [submodule "platform/broadcom/sonic-platform-modules-accton"] path = platform/broadcom/sonic-platform-modules-accton url = https://github.com/edge-core/sonic-platform-modules-accton.git +[submodule "src/tacacs/sonic-pam-tacplus"] + path = src/tacacs/sonic-pam-tacplus + url = https://github.com/liuqu/sonic-pam-tacplus.git +[submodule "src/tacacs/sonic-nss-tacplus"] + path = src/tacacs/sonic-nss-tacplus + url = https://github.com/liuqu/sonic-nss-tacplus.git diff --git a/build_debian.sh b/build_debian.sh index 71f849151a94..e1f4f9552b9e 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -3,12 +3,14 @@ ## an ONIE installer image. ## ## USAGE: -## ./build_debian USERNAME PASSWORD_ENCRYPTED +## ./build_debian USERNAME PASSWORD_ENCRYPTED SONIC_CONFIG_DEBUG ## PARAMETERS: ## USERNAME ## The name of the default admin user ## PASSWORD_ENCRYPTED ## The encrypted password, expected by chpasswd command +## SONIC_CONFIG_DEBUG +## The debug packages install flag ## Default user USERNAME=$1 @@ -158,6 +160,13 @@ sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G sudo,docker $USERNAME -c "$DEFAUL ## Create password for the default user echo $USERNAME:$PASSWORD_ENCRYPTED | sudo LANG=C chroot $FILESYSTEM_ROOT chpasswd -e +## Create remote user +## TODO: remote_user's login shell will be changed to cli shell. +sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G docker "remote_user" -u 1001 -g 999 -c \ + "remote user" -d /home/remote_user -m -s /bin/rbash +sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G sudo,docker "remote_user_su" -u 1002 -g 1000 -c \ + "remote sudo user" -d /home/remote_user_su -m -s /bin/bash + ## Pre-install hardware drivers sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install \ firmware-linux-nonfree diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index 647a73b5ce1c..0448a25c1233 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -89,6 +89,16 @@ sudo cp -f $IMAGE_CONFIGS/bash/bash.bashrc $FILESYSTEM_ROOT/etc/ sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/sonic-device-data_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f +# Install pam-tacplus +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libtac2_1.4.1-1_amd64.deb +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libpam-tacplus_1.4.1-1_amd64.deb + +# Install nss-tacplus +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libnss-tacplus_1.0.3-1_amd64.deb + +# Copy aaa configuration files +sudo cp -f $IMAGE_CONFIGS/aaa/aaa.json $FILESYSTEM_ROOT/etc/sonic/ + # Copy crontabs sudo cp -f $IMAGE_CONFIGS/cron.d/* $FILESYSTEM_ROOT/etc/cron.d/ diff --git a/files/image_config/aaa/aaa.json b/files/image_config/aaa/aaa.json new file mode 100644 index 000000000000..6d8a2d91696e --- /dev/null +++ b/files/image_config/aaa/aaa.json @@ -0,0 +1,13 @@ +{ + "debug": true, + "src_ip": "", + "authentication": { + "login": { + "pam_priority": [ + "local" + ] + } + }, + "tacacs_server_list": {}, + "fail_through": false +} diff --git a/rules/tacacs.mk b/rules/tacacs.mk new file mode 100644 index 000000000000..68731490f185 --- /dev/null +++ b/rules/tacacs.mk @@ -0,0 +1,29 @@ +# libpam-tacplus packages + +PAM_TACPLUS_VERSION = 1.4.1-1 + +LIBTAC2 = libtac2_$(PAM_TACPLUS_VERSION)_amd64.deb +$(LIBTAC2)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-pam-tacplus +SONIC_DPKG_DEBS += $(LIBTAC2) + +LIBPAM_TACPLUS = libpam-tacplus_$(PAM_TACPLUS_VERSION)_amd64.deb +$(LIBPAM_TACPLUS)_RDEPENDS += $(LIBTAC2) +$(LIBPAM_TACPLUS)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-pam-tacplus +SONIC_DPKG_DEBS += $(LIBPAM_TACPLUS) +$(eval $(call add_derived_package,$(LIBTAC2),$(LIBPAM_TACPLUS))) + +LIBTAC_DEV = libtac-dev_$(PAM_TACPLUS_VERSION)_amd64.deb +$(LIBTAC_DEV)_RDEPENDS += $(LIBTAC2) +$(eval $(call add_derived_package,$(LIBTAC2),$(LIBTAC_DEV))) + +# libnss-tacplus packages + +NSS_TACPLUS_VERSION = 1.0.3-1 + +LIBNSS_TACPLUS = libnss-tacplus_$(NSS_TACPLUS_VERSION)_amd64.deb +$(LIBNSS_TACPLUS)_DEPENDS += $(LIBTAC_DEV) +$(LIBNSS_TACPLUS)_RDEPENDS += $(LIBTAC2) +$(LIBNSS_TACPLUS)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-nss-tacplus +SONIC_DPKG_DEBS += $(LIBNSS_TACPLUS) +$(eval $(call add_derived_package,$(LIBTAC2))) + diff --git a/slave.mk b/slave.mk index c8ffbd8086f0..ee888600dadc 100644 --- a/slave.mk +++ b/slave.mk @@ -329,7 +329,7 @@ $(DOCKER_LOAD_TARGETS) : $(TARGET_PATH)/%.gz-load : .platform docker-start $$(TA ############################################################################### # targets for building installers with base image -$(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : .platform onie-image.conf $$(addprefix $(DEBS_PATH)/,$$($$*_DEPENDS)) $$(addprefix $(DEBS_PATH)/,$$($$*_INSTALLS)) $(addprefix $(DEBS_PATH)/,$(INITRAMFS_TOOLS) $(LINUX_KERNEL) $(IGB_DRIVER) $(SONIC_DEVICE_DATA) $(SONIC_UTILS)) $$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) $$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE)) +$(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : .platform onie-image.conf $$(addprefix $(DEBS_PATH)/,$$($$*_DEPENDS)) $$(addprefix $(DEBS_PATH)/,$$($$*_INSTALLS)) $(addprefix $(DEBS_PATH)/,$(INITRAMFS_TOOLS) $(LINUX_KERNEL) $(IGB_DRIVER) $(SONIC_DEVICE_DATA) $(SONIC_UTILS) $(LIBPAM_TACPLUS) $(LIBNSS_TACPLUS)) $$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) $$(addprefix $(PYTHON_WHEELS_PATH)/,$(SONIC_CONFIG_ENGINE)) $(HEADER) ## Pass initramfs and linux kernel explicitly. They are used for all platforms export initramfs_tools="$(DEBS_PATH)/$(INITRAMFS_TOOLS)" @@ -370,7 +370,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : .platform chmod +x sonic_debian_extension.sh, ) - ./build_debian.sh "$(USERNAME)" "$(shell perl -e 'print crypt("$(PASSWORD)", "salt"),"\n"')" $(LOG) + ./build_debian.sh "$(USERNAME)" "$(shell perl -e 'print crypt("$(PASSWORD)", "salt"),"\n"')" "$(SONIC_CONFIG_DEBUG)" $(LOG) TARGET_MACHINE=$($*_MACHINE) IMAGE_TYPE=$($*_IMAGE_TYPE) ./build_image.sh $(LOG) $(foreach docker, $($*_DOCKERS), \ diff --git a/sonic-slave/Dockerfile b/sonic-slave/Dockerfile index ee37f57ee0b5..860ed350d9c6 100644 --- a/sonic-slave/Dockerfile +++ b/sonic-slave/Dockerfile @@ -198,6 +198,8 @@ RUN apt-get update && apt-get install -y \ python-yaml \ # For lockfile procmail \ +# For pam-tacplus build + autoconf-archive \ # For gtest libgtest-dev \ cmake \ diff --git a/src/tacacs/sonic-nss-tacplus b/src/tacacs/sonic-nss-tacplus new file mode 160000 index 000000000000..e29533dd145a --- /dev/null +++ b/src/tacacs/sonic-nss-tacplus @@ -0,0 +1 @@ +Subproject commit e29533dd145aa416a719f1dc07bcde2c7d83e66c diff --git a/src/tacacs/sonic-pam-tacplus b/src/tacacs/sonic-pam-tacplus new file mode 160000 index 000000000000..9c35c35caee9 --- /dev/null +++ b/src/tacacs/sonic-pam-tacplus @@ -0,0 +1 @@ +Subproject commit 9c35c35caee939fd49f63b5042121c194bec55ce From b23b0d0cae68252a8cb9ff862a8cdd51cd74ab98 Mon Sep 17 00:00:00 2001 From: Liuqu Date: Fri, 23 Jun 2017 00:33:22 -0700 Subject: [PATCH 2/7] [TACACS+]: Update submodule pointer --- src/tacacs/sonic-nss-tacplus | 2 +- src/tacacs/sonic-pam-tacplus | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tacacs/sonic-nss-tacplus b/src/tacacs/sonic-nss-tacplus index e29533dd145a..93183d347a83 160000 --- a/src/tacacs/sonic-nss-tacplus +++ b/src/tacacs/sonic-nss-tacplus @@ -1 +1 @@ -Subproject commit e29533dd145aa416a719f1dc07bcde2c7d83e66c +Subproject commit 93183d347a83da534405b9905d4b67d36870659a diff --git a/src/tacacs/sonic-pam-tacplus b/src/tacacs/sonic-pam-tacplus index 9c35c35caee9..6aab5b6cafb6 160000 --- a/src/tacacs/sonic-pam-tacplus +++ b/src/tacacs/sonic-pam-tacplus @@ -1 +1 @@ -Subproject commit 9c35c35caee939fd49f63b5042121c194bec55ce +Subproject commit 6aab5b6cafb65763e297f99e889418677528cd35 From a576065709870cc4f8868ce6f9d3f70d8da03c3d Mon Sep 17 00:00:00 2001 From: Liuqu Date: Fri, 23 Jun 2017 06:11:04 -0700 Subject: [PATCH 3/7] [TACACS+]:Update sonic-utilities submodule --- src/sonic-utilities | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sonic-utilities b/src/sonic-utilities index 0ad528043019..a2e9ebf3c844 160000 --- a/src/sonic-utilities +++ b/src/sonic-utilities @@ -1 +1 @@ -Subproject commit 0ad52804301903664dfe8f84ef1666101c950cd2 +Subproject commit a2e9ebf3c844430013aa3a65fa5e4eaf08163154 From 92cc06556d4f3a1042a67d29adf844e012abf4e5 Mon Sep 17 00:00:00 2001 From: Liuqu Date: Fri, 23 Jun 2017 06:24:06 -0700 Subject: [PATCH 4/7] [build]: Delete unnecessary configuration --- build_debian.sh | 4 +--- slave.mk | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build_debian.sh b/build_debian.sh index e1f4f9552b9e..c4bc29115d40 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -3,14 +3,12 @@ ## an ONIE installer image. ## ## USAGE: -## ./build_debian USERNAME PASSWORD_ENCRYPTED SONIC_CONFIG_DEBUG +## ./build_debian USERNAME PASSWORD_ENCRYPTED ## PARAMETERS: ## USERNAME ## The name of the default admin user ## PASSWORD_ENCRYPTED ## The encrypted password, expected by chpasswd command -## SONIC_CONFIG_DEBUG -## The debug packages install flag ## Default user USERNAME=$1 diff --git a/slave.mk b/slave.mk index ee888600dadc..b2a6173ab548 100644 --- a/slave.mk +++ b/slave.mk @@ -370,7 +370,7 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : .platform chmod +x sonic_debian_extension.sh, ) - ./build_debian.sh "$(USERNAME)" "$(shell perl -e 'print crypt("$(PASSWORD)", "salt"),"\n"')" "$(SONIC_CONFIG_DEBUG)" $(LOG) + ./build_debian.sh "$(USERNAME)" "$(shell perl -e 'print crypt("$(PASSWORD)", "salt"),"\n"')" $(LOG) TARGET_MACHINE=$($*_MACHINE) IMAGE_TYPE=$($*_IMAGE_TYPE) ./build_image.sh $(LOG) $(foreach docker, $($*_DOCKERS), \ From e394b9287f8a7d4d0d41dbe600d17e96298506bd Mon Sep 17 00:00:00 2001 From: Liuqu Date: Wed, 26 Jul 2017 12:08:39 +0800 Subject: [PATCH 5/7] [TACACS+]: Modify tacacs make rule * Delete sonic-nss-tacplus and sonic-pam-tacplus submodule * The nss-tacplus plugin is applied as patch for pam-tacplus * Create remote user in aaa_config when TACACS+ enable, not default in build image * Remove TACACS+ default config file, load default config by aaa config --- build_debian.sh | 7 - .../build_templates/sonic_debian_extension.j2 | 13 +- files/image_config/aaa/aaa.json | 13 - rules/tacacs.mk | 28 +- ...001-Add-config-for-source-ip-address.patch | 228 +++++ src/tacacs/0002-Add-nss-tacplus.patch | 796 ++++++++++++++++++ src/tacacs/Makefile | 26 + src/tacacs/sonic-nss-tacplus | 1 - src/tacacs/sonic-pam-tacplus | 1 - 9 files changed, 1063 insertions(+), 50 deletions(-) delete mode 100644 files/image_config/aaa/aaa.json create mode 100644 src/tacacs/0001-Add-config-for-source-ip-address.patch create mode 100644 src/tacacs/0002-Add-nss-tacplus.patch create mode 100644 src/tacacs/Makefile delete mode 160000 src/tacacs/sonic-nss-tacplus delete mode 160000 src/tacacs/sonic-pam-tacplus diff --git a/build_debian.sh b/build_debian.sh index e5cd97f17436..ff86cb22de45 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -158,13 +158,6 @@ sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G sudo,docker $USERNAME -c "$DEFAUL ## Create password for the default user echo $USERNAME:$PASSWORD_ENCRYPTED | sudo LANG=C chroot $FILESYSTEM_ROOT chpasswd -e -## Create remote user -## TODO: remote_user's login shell will be changed to cli shell. -sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G docker "remote_user" -u 1001 -g 999 -c \ - "remote user" -d /home/remote_user -m -s /bin/rbash -sudo LANG=C chroot $FILESYSTEM_ROOT useradd -G sudo,docker "remote_user_su" -u 1002 -g 1000 -c \ - "remote sudo user" -d /home/remote_user_su -m -s /bin/bash - ## Pre-install hardware drivers sudo LANG=C chroot $FILESYSTEM_ROOT apt-get -y install \ firmware-linux-nonfree diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index a0f9b9229373..77762c333aaf 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -89,15 +89,10 @@ sudo cp -f $IMAGE_CONFIGS/bash/bash.bashrc $FILESYSTEM_ROOT/etc/ sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/sonic-device-data_*.deb || \ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f -# Install pam-tacplus -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libtac2_1.4.1-1_amd64.deb -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libpam-tacplus_1.4.1-1_amd64.deb - -# Install nss-tacplus -sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libnss-tacplus_1.0.3-1_amd64.deb - -# Copy aaa configuration files -sudo cp -f $IMAGE_CONFIGS/aaa/aaa.json $FILESYSTEM_ROOT/etc/sonic/ +# Install pam-tacplus and nss-tacplus +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libtac2_*.deb +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libpam-tacplus_*.deb +sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/libnss-tacplus_*.deb # Copy crontabs sudo cp -f $IMAGE_CONFIGS/cron.d/* $FILESYSTEM_ROOT/etc/cron.d/ diff --git a/files/image_config/aaa/aaa.json b/files/image_config/aaa/aaa.json deleted file mode 100644 index 6d8a2d91696e..000000000000 --- a/files/image_config/aaa/aaa.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "debug": true, - "src_ip": "", - "authentication": { - "login": { - "pam_priority": [ - "local" - ] - } - }, - "tacacs_server_list": {}, - "fail_through": false -} diff --git a/rules/tacacs.mk b/rules/tacacs.mk index 68731490f185..ee65a6ee6d63 100644 --- a/rules/tacacs.mk +++ b/rules/tacacs.mk @@ -2,28 +2,18 @@ PAM_TACPLUS_VERSION = 1.4.1-1 -LIBTAC2 = libtac2_$(PAM_TACPLUS_VERSION)_amd64.deb -$(LIBTAC2)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-pam-tacplus -SONIC_DPKG_DEBS += $(LIBTAC2) +export PAM_TACPLUS_VERSION LIBPAM_TACPLUS = libpam-tacplus_$(PAM_TACPLUS_VERSION)_amd64.deb -$(LIBPAM_TACPLUS)_RDEPENDS += $(LIBTAC2) -$(LIBPAM_TACPLUS)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-pam-tacplus -SONIC_DPKG_DEBS += $(LIBPAM_TACPLUS) -$(eval $(call add_derived_package,$(LIBTAC2),$(LIBPAM_TACPLUS))) - -LIBTAC_DEV = libtac-dev_$(PAM_TACPLUS_VERSION)_amd64.deb -$(LIBTAC_DEV)_RDEPENDS += $(LIBTAC2) -$(eval $(call add_derived_package,$(LIBTAC2),$(LIBTAC_DEV))) +$(LIBPAM_TACPLUS)_SRC_PATH = $(SRC_PATH)/tacacs +SONIC_MAKE_DEBS += $(LIBPAM_TACPLUS) -# libnss-tacplus packages +LIBTAC2 = libtac2_$(PAM_TACPLUS_VERSION)_amd64.deb +$(eval $(call add_derived_package,$(LIBPAM_TACPLUS),$(LIBTAC2))) -NSS_TACPLUS_VERSION = 1.0.3-1 +LIBTAC_DEV = libtac-dev_$(PAM_TACPLUS_VERSION)_amd64.deb +$(eval $(call add_derived_package,$(LIBPAM_TACPLUS),$(LIBTAC_DEV))) -LIBNSS_TACPLUS = libnss-tacplus_$(NSS_TACPLUS_VERSION)_amd64.deb -$(LIBNSS_TACPLUS)_DEPENDS += $(LIBTAC_DEV) +LIBNSS_TACPLUS = libnss-tacplus_$(PAM_TACPLUS_VERSION)_amd64.deb $(LIBNSS_TACPLUS)_RDEPENDS += $(LIBTAC2) -$(LIBNSS_TACPLUS)_SRC_PATH = $(SRC_PATH)/tacacs/sonic-nss-tacplus -SONIC_DPKG_DEBS += $(LIBNSS_TACPLUS) -$(eval $(call add_derived_package,$(LIBTAC2))) - +$(eval $(call add_derived_package,$(LIBPAM_TACPLUS),$(LIBNSS_TACPLUS))) diff --git a/src/tacacs/0001-Add-config-for-source-ip-address.patch b/src/tacacs/0001-Add-config-for-source-ip-address.patch new file mode 100644 index 000000000000..0b711ac8118d --- /dev/null +++ b/src/tacacs/0001-Add-config-for-source-ip-address.patch @@ -0,0 +1,228 @@ +From 6aab5b6cafb65763e297f99e889418677528cd35 Mon Sep 17 00:00:00 2001 +From: Liuqu +Date: Thu, 22 Jun 2017 23:09:54 -0700 +Subject: [PATCH] Add config for source ip address + +* Support to set source ip address for tacacs +* Modify libpam-tacplus postinstall, don't update pam-auth temporarily + when libpam-tacplus install +* Fix libtac2-bin dpkg-build error +--- + debian/libpam-tacplus.postinst | 2 +- + debian/libtac2-bin.install | 2 +- + debian/tacplus | 4 ---- + pam_tacplus.c | 21 ++++++++++++++++----- + support.c | 27 ++++++++++++++++++++++++++- + support.h | 2 ++ + 6 files changed, 46 insertions(+), 12 deletions(-) + +diff --git a/debian/libpam-tacplus.postinst b/debian/libpam-tacplus.postinst +index 7e37590..b008b7a 100644 +--- a/debian/libpam-tacplus.postinst ++++ b/debian/libpam-tacplus.postinst +@@ -2,6 +2,6 @@ + + set -e + +-pam-auth-update --package ++#pam-auth-update --package + + #DEBHELPER# +diff --git a/debian/libtac2-bin.install b/debian/libtac2-bin.install +index 236670a..1df36c6 100644 +--- a/debian/libtac2-bin.install ++++ b/debian/libtac2-bin.install +@@ -1 +1 @@ +-usr/sbin ++usr/bin/* +diff --git a/debian/tacplus b/debian/tacplus +index 5296cf6..985395e 100644 +--- a/debian/tacplus ++++ b/debian/tacplus +@@ -3,13 +3,9 @@ Default: yes + Priority: 257 + Auth-Type: Primary + Auth: +- sufficient pam_tacplus.so + Account-Type: Primary + Account: +- sufficient pam_tacplus.so + Password-Type: Primary + Password: +- sufficient pam_tacplus.so + Session-Type: Additional + Session: +- optional pam_tacplus.so +diff --git a/pam_tacplus.c b/pam_tacplus.c +index 324cd5d..b9188b8 100644 +--- a/pam_tacplus.c ++++ b/pam_tacplus.c +@@ -134,6 +134,7 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, + char *typemsg; + int status = PAM_SESSION_ERR; + int srv_i, tac_fd; ++ struct addrinfo *source_address = NULL; + + typemsg = tac_acct_flag2str(type); + ctrl = _pam_parse(argc, argv); +@@ -185,8 +186,9 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, + + status = PAM_SESSION_ERR; + for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { ++ set_source_ip(tac_source_ip, &source_address); + tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, +- NULL, tac_timeout); ++ source_address, tac_timeout); + if (tac_fd < 0) { + _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, + typemsg); +@@ -240,6 +242,8 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, + int srv_i; + int tac_fd, status, msg, communicating; + ++ struct addrinfo *source_address = NULL; ++ + user = pass = tty = r_addr = NULL; + + ctrl = _pam_parse(argc, argv); +@@ -281,13 +285,16 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); + ++ /* set the source ip address for the tacacs packets */ ++ set_source_ip(tac_source_ip, &source_address); ++ + status = PAM_AUTHINFO_UNAVAIL; + for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i); + + tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, +- NULL, tac_timeout); ++ source_address, tac_timeout); + if (tac_fd < 0) { + _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i); + active_server.addr = NULL; +@@ -548,6 +555,7 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, + struct areply arep; + struct tac_attrib *attr = NULL; + int tac_fd; ++ struct addrinfo *source_address = NULL; + + flags = flags; /* unused */ + +@@ -606,9 +614,10 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, + tac_add_attrib(&attr, "service", tac_service); + if (tac_protocol[0] != '\0') + tac_add_attrib(&attr, "protocol", tac_protocol); +- +- tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL, ++ set_source_ip(tac_source_ip, &source_address); ++ tac_fd = tac_connect_single(active_server.addr, active_server.key, source_address, + tac_timeout); ++ + if (tac_fd < 0) { + _pam_log(LOG_ERR, "TACACS+ server unavailable"); + if (arep.msg != NULL) +@@ -751,6 +760,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, + const void *pam_pass = NULL; + int srv_i; + int tac_fd, status, msg, communicating; ++ struct addrinfo *source_address = NULL; + + user = pass = tty = r_addr = NULL; + +@@ -800,8 +810,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, + if (ctrl & PAM_TAC_DEBUG) + syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i); + ++ set_source_ip(tac_source_ip, &source_address); + tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, +- NULL, tac_timeout); ++ source_address, tac_timeout); + if (tac_fd < 0) { + _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i); + continue; +diff --git a/support.c b/support.c +index 2406b32..41a45af 100644 +--- a/support.c ++++ b/support.c +@@ -39,6 +39,7 @@ char tac_prompt[64]; + struct addrinfo tac_srv_addr[TAC_PLUS_MAXSERVERS]; + struct sockaddr tac_sock_addr[TAC_PLUS_MAXSERVERS]; + char tac_srv_key[TAC_PLUS_MAXSERVERS][TAC_SECRET_MAX_LEN+1]; ++char tac_source_ip[64]; + + void _pam_log(int err, const char *format,...) { + char msg[256]; +@@ -228,7 +229,7 @@ int _pam_parse (int argc, const char **argv) { + tac_protocol[0] = 0; + tac_prompt[0] = 0; + tac_login[0] = 0; +- ++ tac_source_ip[0] = 0; + for (ctrl = 0; argc-- > 0; ++argv) { + if (!strcmp (*argv, "debug")) { /* all */ + ctrl |= PAM_TAC_DEBUG; +@@ -318,6 +319,9 @@ int _pam_parse (int argc, const char **argv) { + } else { + tac_readtimeout_enable = 1; + } ++ } else if (!strncmp (*argv, "source_ip=", strlen("source_ip="))) { ++ /* source ip for the packets */ ++ strncpy (tac_source_ip, *argv + strlen("source_ip="), sizeof(tac_source_ip)); + } else { + _pam_log (LOG_WARNING, "unrecognized option: %s", *argv); + } +@@ -336,8 +340,29 @@ int _pam_parse (int argc, const char **argv) { + _pam_log(LOG_DEBUG, "tac_protocol='%s'", tac_protocol); + _pam_log(LOG_DEBUG, "tac_prompt='%s'", tac_prompt); + _pam_log(LOG_DEBUG, "tac_login='%s'", tac_login); ++ _pam_log(LOG_DEBUG, "tac_source_ip='%s'", tac_source_ip); + } + + return ctrl; + } /* _pam_parse */ + ++/* set source ip address for the outgoing tacacs packets */ ++void set_source_ip(const char *tac_source_ip, ++ struct addrinfo **source_address) { ++ ++ struct addrinfo hints; ++ int rv; ++ ++ /* set the source ip address for the tacacs packets */ ++ if (tac_source_ip != NULL) { ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ if ((rv = getaddrinfo(tac_source_ip, NULL, &hints, ++ source_address)) != 0) { ++ _pam_log(LOG_ERR, "error setting the source ip information"); ++ } else { ++ _pam_log(LOG_DEBUG, "source ip is set"); ++ } ++ } ++} +diff --git a/support.h b/support.h +index 07c2f9d..4217618 100644 +--- a/support.h ++++ b/support.h +@@ -39,6 +39,7 @@ extern int tac_srv_no; + extern char tac_service[64]; + extern char tac_protocol[64]; + extern char tac_prompt[64]; ++extern char tac_source_ip[64]; + void tac_copy_addr_info (struct addrinfo *p_dst, const struct addrinfo *p_src); + + int _pam_parse (int, const char **); +@@ -52,5 +53,6 @@ char *_pam_get_user(pam_handle_t *); + char *_pam_get_terminal(pam_handle_t *); + char *_pam_get_rhost(pam_handle_t *); + ++void set_source_ip(const char *tac_source_ip, struct addrinfo **source_address); + #endif /* PAM_TACPLUS_SUPPORT_H */ + +-- +2.7.4 + diff --git a/src/tacacs/0002-Add-nss-tacplus.patch b/src/tacacs/0002-Add-nss-tacplus.patch new file mode 100644 index 000000000000..101fe6b6a80b --- /dev/null +++ b/src/tacacs/0002-Add-nss-tacplus.patch @@ -0,0 +1,796 @@ +From c845bcd8dedb5ac643bf9457af5eed73120e4d7c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E5=85=AD=E6=9B=B2?= +Date: Thu, 6 Jul 2017 05:38:24 -0700 +Subject: [PATCH] Add nss-tacplus + +--- + Makefile.am | 37 ++ + configure.ac | 1 + + debian/control | 6 + + debian/libnss-tacplus.install | 2 + + debian/libnss-tacplus.lintian-overrides | 8 + + debian/libnss-tacplus.postinst | 32 ++ + debian/libnss-tacplus.symbols | 2 + + debian/rules | 2 + + nss_tacplus.c | 598 ++++++++++++++++++++++++++++++++ + tacplus_nss.conf | 1 + + 10 files changed, 689 insertions(+) + create mode 100644 debian/libnss-tacplus.install + create mode 100644 debian/libnss-tacplus.lintian-overrides + create mode 100644 debian/libnss-tacplus.postinst + create mode 100644 debian/libnss-tacplus.symbols + create mode 100644 nss_tacplus.c + create mode 100644 tacplus_nss.conf + +diff --git a/Makefile.am b/Makefile.am +index 7e09e98..0fd489d 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -64,6 +64,18 @@ pam_tacplus_la_CFLAGS = $(AM_CFLAGS) -I $(top_srcdir)/libtac/include -I $(top_sr + pam_tacplus_la_LDFLAGS = -module -avoid-version + pam_tacplus_la_LIBADD = libtac.la + ++libnssdir = /lib/$(DEB_HOST_MULTIARCH) ++libnss_LTLIBRARIES = libnss_tacplus.la ++ ++libnss_tacplus_la_SOURCES = \ ++nss_tacplus.c ++ ++libnss_tacplus_la_CFLAGS = $(AM_CFLAGS) ++# Version 2.0 because that's the NSS module version, and they must match ++libnss_tacplus_la_LDFLAGS = -module -version-info 2:0:0 -shared ++libnss_tacplus_la_LIBADD = -ltac ++ ++ + EXTRA_DIST = pam_tacplus.spec libtac.pc.in + if DOC + dist_doc_DATA = sample.pam README.md AUTHORS ChangeLog +@@ -82,3 +94,28 @@ coverity: + tar cvzf build.tar.gz cov-int/ + + .PHONY: coverity ++ ++MULTI_OS_DIRECTORY=$(shell $(CC) $(CFLAGS) -print-multiarch) ++# This and the install rules using it are copied from libnss-ldap-264 ++LIBC_VERS = $(shell ls /lib/$(MULTI_OS_DIRECTORY)/libc-*.so | sed -e '1s|.*libc-\(.*\)\.so|\1|') ++NSS_TACPLUS_LIBC_VERSIONED = libnss_tacplus-$(LIBC_VERS).so ++ ++NSS_VERS = $(shell ls /lib/$(MULTI_OS_DIRECTORY)/libnss_files.so.? | sed -e '1s|.*libnss_files\.so\.\(.*\)|\1|') ++NSS_TACPLUS_NSS_VERSIONED = libnss_tacplus.so.$(NSS_VERS) ++ ++# strip all but the NSS entry point, to avoid symbol pollution ++# nobody will link against this plugin, so no need for .la ++# for NSS, we don't need to install the libnss_tacplus.so.2.0.0 ++# and don't want libnss_tacplus.so either since the library is a plugin. ++# libtool installs both automatically, so we remove them. ++# Copying debian and installing main copy as file with libc version, ++# and the .so.2 version as a symlink to the libc versioned file ++install-data-hook: ++ rm -f $(DESTDIR)$(libnssdir)/libnss_tacplus.la ++ rm -f $(DESTDIR)$(libnssdir)/libnss_tacplus.so $(DESTDIR)$(libnssdir)/libnss_tacplus.so.2.0.0 ++ $(mkinstalldirs) $(DESTDIR)$(libnssdir) $(DESTDIR)$(sysconfdir) ++ cd .libs && $(INSTALL_PROGRAM) libnss_tacplus.so $(DESTDIR)$(libnssdir)/$(NSS_TACPLUS_LIBC_VERSIONED) ++ $(STRIP) --keep-symbol=_nss_tacplus_getpwnam_r $(DESTDIR)$(libnssdir)/$(NSS_TACPLUS_LIBC_VERSIONED) ++ cd $(DESTDIR)$(libnssdir); ln -sf $(NSS_TACPLUS_LIBC_VERSIONED) $(NSS_TACPLUS_NSS_VERSIONED) ++ ${INSTALL} -m 644 tacplus_nss.conf $(DESTDIR)$(sysconfdir) ++ +diff --git a/configure.ac b/configure.ac +index e34c769..bd683a8 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -49,6 +49,7 @@ dnl Checks for header files. + AC_HEADER_STDC + AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h sys/time.h ]) + AC_CHECK_HEADERS([syslog.h unistd.h openssl/md5.h openssl/rand.h linux/random.h sys/random.h]) ++AC_CHECK_HEADERS([nss.h]) + AC_CHECK_HEADER(security/pam_appl.h, [], [AC_MSG_ERROR([PAM libraries missing. Install with "yum install pam-devel" or "apt-get install libpam-dev".])] ) + AM_CONDITIONAL(MY_MD5, [test "$ac_cv_header_openssl_md5_h" = "no" ]) + AM_CONDITIONAL(TACC, [test "$ac_cv_lib_crypto_RAND_pseudo_bytes" = "yes"]) +diff --git a/debian/control b/debian/control +index 2e851b1..1e0b25d 100644 +--- a/debian/control ++++ b/debian/control +@@ -34,3 +34,9 @@ Depends: ${misc:Depends}, libtac2 (= ${binary:Version}), libc6-dev|libc-dev + Description: Development files for TACACS+ protocol library + Contains C header files and development files for libtac, a TACACS+ protocol + implementation. ++ ++Package: libnss-tacplus ++Architecture: any ++Depends: ${shlibs:Depends}, ${misc:Depends}, libtac2 (= ${binary:Version}) ++Description: NSS module for TACACS+ authentication without local passwd entry ++ Performs getpwname lookups via NSS for users logged in via tacacs authentication. +diff --git a/debian/libnss-tacplus.install b/debian/libnss-tacplus.install +new file mode 100644 +index 0000000..9e88b1b +--- /dev/null ++++ b/debian/libnss-tacplus.install +@@ -0,0 +1,2 @@ ++lib/*/*.so* ++etc/tacplus_nss.conf +diff --git a/debian/libnss-tacplus.lintian-overrides b/debian/libnss-tacplus.lintian-overrides +new file mode 100644 +index 0000000..4ac1cba +--- /dev/null ++++ b/debian/libnss-tacplus.lintian-overrides +@@ -0,0 +1,8 @@ ++libnss-tacplus binary package-name-doesnt-match-sonames libnss-tacplus2 ++libnss-tacplus package-name-doesnt-match-sonames libnss-tacplus2 ++libnss-tacplus source native-package-with-dash-version ++libnss-tacplus source diff-contains-git-control-dir .git ++libnss-tacplus source unsupported-source-format 3.0 (git) ++libnss-tacplus source changelog-should-mention-nmu ++libnss-tacplus source source-nmu-has-incorrect-version-number 1.0.1-1 ++libnss-tacplus new-package-should-close-itp-bu +diff --git a/debian/libnss-tacplus.postinst b/debian/libnss-tacplus.postinst +new file mode 100644 +index 0000000..8b3a74e +--- /dev/null ++++ b/debian/libnss-tacplus.postinst +@@ -0,0 +1,32 @@ ++#!/bin/sh ++# postinst script for libnss-tacplus ++# ++# see: dh_installdeb(1) ++ ++set -e ++ ++case "$1" in ++ configure) ++ ;; ++ ++ abort-upgrade|abort-remove|abort-deconfigure) ++ ;; ++ ++ *) ++ echo "postinst called with unknown argument \`$1'" >&2 ++ exit 1 ++ ;; ++esac ++ ++# Add tacplus to /etc/nsswitch.conf, since it's necessary ++# for this package, and won't break anything else. Do nothing ++# if tacplus is already present in the passwd line ++if [ -e "/etc/nsswitch.conf" ]; then ++ sed -i -e '/tacplus/b' \ ++ -e '/^passwd/s/compat/& tacplus/' /etc/nsswitch.conf ++fi ++ ++ ++#DEBHELPER# ++ ++exit 0 +diff --git a/debian/libnss-tacplus.symbols b/debian/libnss-tacplus.symbols +new file mode 100644 +index 0000000..f476e7d +--- /dev/null ++++ b/debian/libnss-tacplus.symbols +@@ -0,0 +1,2 @@ ++libnss_tacplus.so.2 libnss-tacplus #MINVER# ++ _nss_tacplus_getpwnam_r@Base 1.0.1 +diff --git a/debian/rules b/debian/rules +index 0fa1f54..c0251b8 100755 +--- a/debian/rules ++++ b/debian/rules +@@ -22,5 +22,7 @@ override_dh_auto_configure: + override_dh_install: + mkdir -p debian/libpam-tacplus/usr/share/pam-configs + cp debian/tacplus debian/libpam-tacplus/usr/share/pam-configs/ ++ # mkdir -p debian/libnss-tacplus/etc ++ # cp debian/tmp/etc/tacplus_nss.conf debian/libnss-tacplus/etc/ + dh_install + +diff --git a/nss_tacplus.c b/nss_tacplus.c +new file mode 100644 +index 0000000..3438db9 +--- /dev/null ++++ b/nss_tacplus.c +@@ -0,0 +1,598 @@ ++/* ++ * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved. ++ * Author: Dave Olson ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program - see the file COPYING. ++ */ ++ ++/* ++ * This plugin implements getpwnam_r for NSS over TACACS+ ++ * and implements getpwuid_r for UIDs if and only if a mapped ++ * TACACS+ user is currently logged in (libtacplus_map) ++ * This means that if you do, e.g.: ls -ld ~tacacs15, you will ++ * sometimes get a mapped username, and other times get tacacs15, ++ * depending on whether a mapped user is logged in or not. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static const char *nssname = "nss_tacplus"; /* for syslogs */ ++static const char *config_file = "/etc/tacplus_nss.conf"; ++ ++/* ++ * pwbuf is used to reduce number of arguments passed around; the strings in ++ * the passwd struct need to point into this buffer. ++ */ ++struct pwbuf { ++ char *name; ++ char *buf; ++ struct passwd *pw; ++ int *errnop; ++ size_t buflen; ++}; ++ ++typedef struct { ++ struct addrinfo *addr; ++ char *key; ++ int timeout; ++} tacplus_server_t; ++ ++enum tac_user { ++ TAC_USER_SU = 0, ++ TAC_USER = 1, ++ TAC_USER_NUM ++}; ++ ++/* set from configuration file parsing */ ++static tacplus_server_t tac_srv[TAC_PLUS_MAXSERVERS]; ++static int tac_srv_no; ++static struct addrinfo *source_addr = NULL; ++static struct passwd tac_pw[TAC_USER_NUM]; ++const char *tac_pw_name[TAC_USER_NUM] = {"remote_user_su", "remote_user"}; ++ ++static char tac_service[] = "shell"; ++static char tac_protocol[] = "ssh"; ++static int debug=1; ++static int conf_parsed = 0; ++ ++static int parse_tac_pw() ++{ ++ FILE *fp; ++ struct passwd *pw; ++ int index, count = 0; ++ ++ fp = fopen("/etc/passwd", "r"); ++ if(NULL == fp) ++ { ++ syslog(LOG_ERR, "%s: /etc/passwd fopen failed", nssname); ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ while(0 != (pw = fgetpwent(fp))) ++ { ++ for(index = TAC_USER_SU; index < TAC_USER_NUM; ++index) ++ { ++ if(!strcmp(pw->pw_name, tac_pw_name[index])) ++ { ++ tac_pw[index].pw_name = strdup(pw->pw_name); ++ tac_pw[index].pw_passwd = strdup(pw->pw_passwd); ++ tac_pw[index].pw_gecos = strdup(pw->pw_gecos); ++ tac_pw[index].pw_dir = strdup(pw->pw_dir); ++ tac_pw[index].pw_shell = strdup(pw->pw_shell); ++ tac_pw[index].pw_uid = pw->pw_uid; ++ tac_pw[index].pw_gid = pw->pw_gid; ++ ++count; ++ if(debug) ++ syslog(LOG_DEBUG, "%s: tac_pw name %s", nssname, ++ tac_pw[index].pw_name); ++ } ++ } ++ } ++ fclose(fp); ++ ++ if(count < TAC_USER_NUM) ++ { ++ syslog(LOG_ERR, "%s: tacacs user passwd parsed %d < %d", nssname, ++ count, TAC_USER_NUM); ++ } ++ ++ return 0; ++} ++ ++static int parse_tac_server(char *srv_buf) ++{ ++ char *token; ++ char delim[] = " ,\t\n\r\f"; ++ ++ token = strsep(&srv_buf, delim); ++ while(NULL != token) ++ { ++ if('\0' != token) ++ { ++ if(!strncmp(token, "server=", 7)) ++ { ++ struct addrinfo hints, *server; ++ int rv; ++ char *srv, *port; ++ ++ memset(&hints, 0, sizeof hints); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ ++ srv = token + 7; ++ port = strchr(srv, ':'); ++ if(NULL != port) ++ { ++ *port = '\0'; ++ port++; ++ } ++ ++ if((rv = getaddrinfo(srv, (port == NULL) ? "49" : port, &hints, ++ &server)) == 0) ++ { ++ if(NULL != server) ++ { ++ if(NULL != tac_srv[tac_srv_no].addr) ++ freeaddrinfo(tac_srv[tac_srv_no].addr); ++ if(NULL != tac_srv[tac_srv_no].key) ++ free(tac_srv[tac_srv_no].key); ++ memset(tac_srv + tac_srv_no, 0, sizeof(tacplus_server_t)); ++ ++ tac_srv[tac_srv_no].addr = server; ++ } ++ else ++ { ++ syslog(LOG_ERR, "%s: server NULL", nssname); ++ } ++ } ++ else ++ { ++ syslog(LOG_ERR, "%s: invalid server: %s (getaddrinfo: %s)", ++ nssname, srv, gai_strerror(rv)); ++ return -1; ++ } ++ } ++ else if(!strncmp(token, "secret=", 7)) ++ { ++ tac_srv[tac_srv_no].key = strdup(token + 7); ++ } ++ else if(!strncmp(token, "timeout=", 8)) ++ { ++ tac_srv[tac_srv_no].timeout = (int)strtoul(token + 8, NULL, 0); ++ if(tac_srv[tac_srv_no].timeout < 0) ++ tac_srv[tac_srv_no].timeout = 0; ++ /* Limit timeout to make sure upper application not wait ++ * for a long time*/ ++ if(tac_srv[tac_srv_no].timeout > 3) ++ tac_srv[tac_srv_no].timeout = 3; ++ } ++ } ++ token = strsep(&srv_buf, delim); ++ } ++ ++ return 0; ++} ++ ++static int parse_config(const char *file) ++{ ++ FILE *fp; ++ char buf[512] = {0}; ++ ++ if(conf_parsed < 1) ++ { ++ if(0 == parse_tac_pw()) ++ conf_parsed = 1; ++ else ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ fp = fopen(file, "r"); ++ if(NULL == fp) ++ { ++ syslog(LOG_ERR, "%s: %s fopen failed", nssname, file); ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ tac_srv_no = 0; ++ while(fgets(buf, sizeof(buf), fp)) ++ { ++ if('#' == *buf || isspace(*buf)) ++ continue; ++ ++ if(!strncmp(buf, "debug=", 6)) ++ { ++ debug = strtoul(buf + 6, NULL, 0); ++ } ++ else if(!strncmp(buf, "src_ip=", 7)) ++ { ++ struct addrinfo hints; ++ char *ip = buf + 7; ++ ++ memset(&hints, 0, sizeof hints); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ ++ if(source_addr) ++ freeaddrinfo(source_addr); ++ ++ if(0 != getaddrinfo(ip, NULL, &hints, &source_addr)) ++ syslog(LOG_ERR, "%s: error setting the source ip information", ++ nssname); ++ } ++ else if(!strncmp(buf, "server=", 7)) ++ { ++ if(TAC_PLUS_MAXSERVERS > tac_srv_no) ++ { ++ if(0 == parse_tac_server(buf)) ++ ++tac_srv_no; ++ } ++ else ++ syslog(LOG_ERR, "%s: tac server num is more than %d", ++ nssname, TAC_PLUS_MAXSERVERS); ++ } ++ } ++ fclose(fp); ++ ++ if(debug) ++ { ++ int n; ++ ++ for(n = 0; n < tac_srv_no; ++n) ++ { ++ syslog(LOG_DEBUG, "%s: server[%d] { addr=%s, key=%s, timeout=%d }", ++ nssname, n, tac_ntop(tac_srv[n].addr->ai_addr), ++ tac_srv[n].key, tac_srv[n].timeout); ++ } ++ ++ syslog(LOG_DEBUG, "%s: src_ip=%s", nssname, NULL == source_addr ++ ? "NULL" : tac_ntop(source_addr->ai_addr)); ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * copy a passwd structure and it's strings, using the provided buffer ++ * for the strings. ++ * if usename is non-NULL, use that, rather than pw_name in srcpw, so we can ++ * preserve the original requested name (this is part of the tacacs remapping). ++ * For strings, if pointer is null, use an empty string. ++ * Returns 0 if everything fit, otherwise 1. ++ */ ++static int ++pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, ++ const char *usename) ++{ ++ size_t needlen; ++ int cnt; ++ ++ if(!usename) ++ usename = srcpw->pw_name; ++ ++ needlen = usename ? strlen(usename) + 1 : 1 + ++ srcpw->pw_dir ? strlen(srcpw->pw_dir) + 1 : 1 + ++ srcpw->pw_gecos ? strlen(srcpw->pw_gecos) + 1 : 1 + ++ srcpw->pw_shell ? strlen(srcpw->pw_shell) + 1 : 1 + ++ srcpw->pw_passwd ? strlen(srcpw->pw_passwd) + 1 : 1; ++ if(needlen > len) { ++ if(debug) ++ syslog(LOG_DEBUG, "%s provided password buffer too small (%ld<%ld)", ++ nssname, (long)len, (long)needlen); ++ return 1; ++ } ++ ++ destpw->pw_uid = srcpw->pw_uid; ++ destpw->pw_gid = srcpw->pw_gid; ++ ++ cnt = snprintf(buf, len, "%s", usename ? usename : ""); ++ destpw->pw_name = buf; ++ cnt++; /* allow for null byte also */ ++ buf += cnt; ++ len -= cnt; ++ /* set pw_passwd "a" for pam_account success */ ++ cnt = snprintf(buf, len, "%s", "a"); ++ destpw->pw_passwd = buf; ++ cnt++; ++ buf += cnt; ++ len -= cnt; ++ cnt = snprintf(buf, len, "%s", srcpw->pw_shell ? srcpw->pw_shell : ""); ++ destpw->pw_shell = buf; ++ cnt++; ++ buf += cnt; ++ len -= cnt; ++ cnt = snprintf(buf, len, "%s", srcpw->pw_gecos ? srcpw->pw_gecos : ""); ++ destpw->pw_gecos = buf; ++ cnt++; ++ buf += cnt; ++ len -= cnt; ++ cnt = snprintf(buf, len, "%s", srcpw->pw_dir ? srcpw->pw_dir : ""); ++ destpw->pw_dir = buf; ++ cnt++; ++ buf += cnt; ++ len -= cnt; ++ ++ return 0; ++} ++ ++/* ++ * we got the user back. Go through the attributes, find their privilege ++ * level, map to the local user, fill in the data, etc. ++ * Returns 0 on success, 1 on errors. ++ */ ++static int ++got_tacacs_user(struct tac_attrib *attr, struct pwbuf *pb) ++{ ++ unsigned long priv_level = 0; ++ int ret; ++ ++ while(attr != NULL) { ++ /* we are looking for the privilege attribute, can be in several forms, ++ * typically priv-lvl= or priv_lvl= */ ++ if(strncasecmp(attr->attr, "priv", 4) == 0) { ++ char *ok, *val; ++ ++ for(val=attr->attr; *val && *val != '*' && *val != '='; val++) ++ ; ++ if(!*val) ++ continue; ++ val++; ++ ++ priv_level = strtoul(val, &ok, 0); ++ ++ /* if this fails, we leave priv_level at 0, which is ++ * least privileged, so that's OK, but at least report it ++ */ ++ if(debug) ++ syslog(LOG_DEBUG, "%s: privilege for %s, (%lu)", ++ nssname, pb->name, priv_level); ++ } ++ attr = attr->next; ++ } ++ ++ if(priv_level == 15) ++ /* remote_user_su has sudo and docker privilege. If it return login ++ * username, user only get gid, can't get group info in /etc/group. ++ * remote_user_su can use all command, and not need to command ++ * authorization by default, so not return login name is ok. ++ * remote_user only has docker privilege, its gid is created as ++ * docker group id. ++ * */ ++ ret = pwcopy(pb->buf, pb->buflen, tac_pw + TAC_USER_SU, pb->pw, NULL); ++ else ++ ret = pwcopy(pb->buf, pb->buflen, tac_pw + TAC_USER, pb->pw, pb->name); ++ ++ if(debug) ++ syslog(LOG_DEBUG, "%s: pw_name=%s, pw_passwd=%s, pw_shell=%s, dir=%s", ++ nssname, pb->pw->pw_name, pb->pw->pw_passwd, pb->pw->pw_shell, ++ pb->pw->pw_dir); ++ ++ if(ret) ++ *pb->errnop = ERANGE; ++ ++ return ret; ++} ++ ++/* ++ * Attempt to connect to the requested tacacs server. ++ * Returns fd for connection, or -1 on failure ++ */ ++ ++static int ++connect_tacacs(struct tac_attrib **attr, int srvr) ++{ ++ int fd; ++ ++ if(!*tac_service) /* reported at config file processing */ ++ return -1; ++ ++ fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, source_addr, ++ tac_srv[srvr].timeout); ++ if(fd >= 0) { ++ *attr = NULL; /* so tac_add_attr() allocates memory */ ++ tac_add_attrib(attr, "service", tac_service); ++ if(tac_protocol[0]) ++ tac_add_attrib(attr, "protocol", tac_protocol); ++ /* empty cmd is required, at least for linux tac_plus */ ++ tac_add_attrib(attr, "cmd", ""); ++ } ++ return fd; ++} ++ ++ ++/* ++ * lookup the user on a TACACS server. Returns 0 on successful lookup, else 1 ++ * ++ * Make a new connection each time, because libtac is single threaded and ++ * doesn't support multiple connects at the same time due to use of globals, ++ * and doesn't have support for persistent connections. That's fixable, but ++ * not worth the effort at this point. ++ * Step through all servers until success or end of list, because different ++ * servers can have different databases. ++ */ ++static int ++lookup_tacacs_user(struct pwbuf *pb) ++{ ++ struct areply arep; ++ int ret = 1, done = 0; ++ struct tac_attrib *attr; ++ int tac_fd, srvr; ++ ++ for(srvr=0; srvr < tac_srv_no && !done; srvr++) { ++ arep.msg = NULL; ++ arep.attr = NULL; ++ arep.status = TAC_PLUS_AUTHOR_STATUS_ERROR; /* if author_send fails */ ++ tac_fd = connect_tacacs(&attr, srvr); ++ if (tac_fd < 0) { ++ if(debug) ++ syslog(LOG_WARNING, "%s: failed to connect TACACS+ server %s," ++ " ret=%d: %m", nssname, tac_srv[srvr].addr ? ++ tac_ntop(tac_srv[srvr].addr->ai_addr) : "unknown", tac_fd); ++ continue; ++ } ++ ret = tac_author_send(tac_fd, pb->name, "", "", attr); ++ if(ret < 0) { ++ if(debug) ++ syslog(LOG_WARNING, "%s: TACACS+ server %s send failed (%d) for" ++ " user %s: %m", nssname, tac_srv[srvr].addr ? ++ tac_ntop(tac_srv[srvr].addr->ai_addr) : "unknown", ret, ++ pb->name); ++ } ++ else { ++ errno = 0; ++ ret = tac_author_read(tac_fd, &arep); ++ if (ret == LIBTAC_STATUS_PROTOCOL_ERR) ++ syslog(LOG_WARNING, "%s: TACACS+ server %s read failed with" ++ " protocol error (incorrect shared secret?) user %s", ++ nssname, tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name); ++ else if (ret < 0) /* ret == 1 OK transaction, use arep.status */ ++ syslog(LOG_WARNING, "%s: TACACS+ server %s read failed (%d) for" ++ " user %s: %m", nssname, ++ tac_ntop(tac_srv[srvr].addr->ai_addr), ret, pb->name); ++ } ++ ++ tac_free_attrib(&attr); ++ close(tac_fd); ++ if(ret < 0) ++ continue; ++ ++ if(arep.status == AUTHOR_STATUS_PASS_ADD || ++ arep.status == AUTHOR_STATUS_PASS_REPL) { ++ ret = got_tacacs_user(arep.attr, pb); ++ if(debug) ++ syslog(LOG_DEBUG, "%s: TACACS+ server %s successful for user %s." ++ " local lookup %s", nssname, ++ tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, ++ ret == 0?"OK":"no match"); ++ done = 1; /* break out of loop after arep cleanup */ ++ } ++ else { ++ ret = 1; /* in case last server */ ++ if(debug) ++ syslog(LOG_DEBUG, "%s: TACACS+ server %s replies user %s" ++ " invalid (%d)", nssname, ++ tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, ++ arep.status); ++ } ++ if(arep.msg) ++ free(arep.msg); ++ if(arep.attr) /* free returned attributes */ ++ tac_free_attrib(&arep.attr); ++ } ++ ++ return ret; ++} ++ ++static bool is_valid_name (const char *name) ++{ ++ /* ++ * User/group names must match [a-z_][a-z0-9_-]*[$] ++ */ ++ if (('\0' == *name) || ++ !((('a' <= *name) && ('z' >= *name)) || ('_' == *name))) { ++ return false; ++ } ++ ++ while ('\0' != *++name) { ++ if (!(( ('a' <= *name) && ('z' >= *name) ) || ++ ( ('0' <= *name) && ('9' >= *name) ) || ++ ('_' == *name) || ++ ('-' == *name) || ++ ( ('$' == *name) && ('\0' == *(name + 1)) ) ++ )) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static bool is_valid_user_name (const char *name) ++{ ++ /* ++ * User names are limited by whatever utmp can ++ * handle. ++ */ ++ if (strlen (name) > 32) { ++ return false; ++ } ++ ++ return is_valid_name (name); ++} ++ ++/* ++ * This is an NSS entry point. ++ * We implement getpwnam(), because we remap from the tacacs login ++ * to the local tacacs0 ... tacacs15 users for all other info, and so ++ * the normal order of "passwd tacplus" (possibly with ldap or anything ++ * else prior to tacplus) will mean we only get used when there isn't ++ * a local user to be found. ++ * ++ * We try the lookup to the tacacs server first. If we can't make a ++ * connection to the server for some reason, we also try looking up ++ * the account name via the mapping file, primarily to handle cases ++ * where we aren't running with privileges to read the tacacs configuration ++ * (since it has the secret key). ++ */ ++enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ enum nss_status status = NSS_STATUS_NOTFOUND; ++ int result; ++ struct pwbuf pbuf; ++ ++ /* When filename completion is used with the tab key in bash, getpwnam ++ is invoked. And the parameter "name" is '*'. In order not to connect to ++ TACACS+ server frequently, check user name whether is valid. */ ++ if(!is_valid_user_name(name)) ++ return NSS_STATUS_NOTFOUND; ++ ++ result = parse_config(config_file); ++ ++ if(result) { /* no config file, no servers, etc. */ ++ /* this is a debug because privileges may not allow access */ ++ if(debug) ++ syslog(LOG_DEBUG, "%s: bad config or server line for nss_tacplus", ++ nssname); ++ } ++ else { ++ /* marshal the args for the lower level functions */ ++ pbuf.name = (char *)name; ++ pbuf.pw = pw; ++ pbuf.buf = buffer; ++ pbuf.buflen = buflen; ++ pbuf.errnop = errnop; ++ ++ if(0 == lookup_tacacs_user(&pbuf)) ++ status = NSS_STATUS_SUCCESS; ++ } ++ ++ if(debug) ++ syslog(LOG_DEBUG, "%s: name=%s, pw_name=%s, pw_passwd=%s, pw_shell=%s", ++ nssname, name, pw->pw_name, pw->pw_passwd, pw->pw_shell); ++ ++ return status; ++} ++ +diff --git a/tacplus_nss.conf b/tacplus_nss.conf +new file mode 100644 +index 0000000..e22794c +--- /dev/null ++++ b/tacplus_nss.conf +@@ -0,0 +1 @@ ++debug=1 +-- +2.7.4 + diff --git a/src/tacacs/Makefile b/src/tacacs/Makefile new file mode 100644 index 000000000000..46395f4e683f --- /dev/null +++ b/src/tacacs/Makefile @@ -0,0 +1,26 @@ +.ONESHELL: +SHELL = /bin/bash +.SHELLFLAGS += -e + +MAIN_TARGET = libpam-tacplus_$(PAM_TACPLUS_VERSION)_amd64.deb +DERIVED_TARGETS = libtac2_$(PAM_TACPLUS_VERSION)_amd64.deb \ + libtac-dev_$(PAM_TACPLUS_VERSION)_amd64.deb \ + libnss-tacplus_$(PAM_TACPLUS_VERSION)_amd64.deb + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + # Obtain pam_tacplus + rm -rf ./pam_tacplus + git clone https://github.com/jeroennijhof/pam_tacplus.git + pushd ./pam_tacplus + git checkout -f 11ac166 + + # Apply patch + git apply ../0001-Add-config-for-source-ip-address.patch + git apply ../0002-Add-nss-tacplus.patch + + dpkg-buildpackage -rfakeroot -b -us -uc + popd + + mv $(DERIVED_TARGETS) $* $(DEST)/ + +$(addprefix $(DEST)/, $(DERIVED_TARGETS)): $(DEST)/% : $(DEST)/$(MAIN_TARGET) diff --git a/src/tacacs/sonic-nss-tacplus b/src/tacacs/sonic-nss-tacplus deleted file mode 160000 index 93183d347a83..000000000000 --- a/src/tacacs/sonic-nss-tacplus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 93183d347a83da534405b9905d4b67d36870659a diff --git a/src/tacacs/sonic-pam-tacplus b/src/tacacs/sonic-pam-tacplus deleted file mode 160000 index 6aab5b6cafb6..000000000000 --- a/src/tacacs/sonic-pam-tacplus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6aab5b6cafb65763e297f99e889418677528cd35 From c5765f9344f6d1c1a0647fe93cfac002620afbd1 Mon Sep 17 00:00:00 2001 From: Liuqu Date: Sun, 20 Aug 2017 23:12:44 -0700 Subject: [PATCH 6/7] [TACACS+]:Update nss patch * Modify the default user mapping policy, create local user for each TACACS+ user. * Add configuration to change the default gid, group and shell of the local user to be created. --- src/tacacs/0002-Add-nss-tacplus.patch | 711 ++++++++++++++++++-------- 1 file changed, 501 insertions(+), 210 deletions(-) diff --git a/src/tacacs/0002-Add-nss-tacplus.patch b/src/tacacs/0002-Add-nss-tacplus.patch index 101fe6b6a80b..d915b99a5efc 100644 --- a/src/tacacs/0002-Add-nss-tacplus.patch +++ b/src/tacacs/0002-Add-nss-tacplus.patch @@ -1,10 +1,13 @@ -From c845bcd8dedb5ac643bf9457af5eed73120e4d7c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?=E5=85=AD=E6=9B=B2?= -Date: Thu, 6 Jul 2017 05:38:24 -0700 -Subject: [PATCH] Add nss-tacplus +From 3a293e757232aa801386b671c21f25cdd14cacbd Mon Sep 17 00:00:00 2001 +From: Liuqu +Date: Wed, 16 Aug 2017 06:24:45 -0700 +Subject: [PATCH 2/2] Add nss-tacplus +* Add nss-tacplus plugin for user role map +* Modify makefile and configure.ac +* Add debian build file and conf file --- - Makefile.am | 37 ++ + Makefile.am | 36 ++ configure.ac | 1 + debian/control | 6 + debian/libnss-tacplus.install | 2 + @@ -12,9 +15,9 @@ Subject: [PATCH] Add nss-tacplus debian/libnss-tacplus.postinst | 32 ++ debian/libnss-tacplus.symbols | 2 + debian/rules | 2 + - nss_tacplus.c | 598 ++++++++++++++++++++++++++++++++ - tacplus_nss.conf | 1 + - 10 files changed, 689 insertions(+) + nss_tacplus.c | 838 ++++++++++++++++++++++++++++++++ + tacplus_nss.conf | 50 ++ + 10 files changed, 977 insertions(+) create mode 100644 debian/libnss-tacplus.install create mode 100644 debian/libnss-tacplus.lintian-overrides create mode 100644 debian/libnss-tacplus.postinst @@ -23,7 +26,7 @@ Subject: [PATCH] Add nss-tacplus create mode 100644 tacplus_nss.conf diff --git a/Makefile.am b/Makefile.am -index 7e09e98..0fd489d 100644 +index 7e09e98..ac3e1f9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,6 +64,18 @@ pam_tacplus_la_CFLAGS = $(AM_CFLAGS) -I $(top_srcdir)/libtac/include -I $(top_sr @@ -45,7 +48,7 @@ index 7e09e98..0fd489d 100644 EXTRA_DIST = pam_tacplus.spec libtac.pc.in if DOC dist_doc_DATA = sample.pam README.md AUTHORS ChangeLog -@@ -82,3 +94,28 @@ coverity: +@@ -82,3 +94,27 @@ coverity: tar cvzf build.tar.gz cov-int/ .PHONY: coverity @@ -73,7 +76,6 @@ index 7e09e98..0fd489d 100644 + $(STRIP) --keep-symbol=_nss_tacplus_getpwnam_r $(DESTDIR)$(libnssdir)/$(NSS_TACPLUS_LIBC_VERSIONED) + cd $(DESTDIR)$(libnssdir); ln -sf $(NSS_TACPLUS_LIBC_VERSIONED) $(NSS_TACPLUS_NSS_VERSIONED) + ${INSTALL} -m 644 tacplus_nss.conf $(DESTDIR)$(sysconfdir) -+ diff --git a/configure.ac b/configure.ac index e34c769..bd683a8 100644 --- a/configure.ac @@ -124,7 +126,7 @@ index 0000000..4ac1cba +libnss-tacplus new-package-should-close-itp-bu diff --git a/debian/libnss-tacplus.postinst b/debian/libnss-tacplus.postinst new file mode 100644 -index 0000000..8b3a74e +index 0000000..b19206e --- /dev/null +++ b/debian/libnss-tacplus.postinst @@ -0,0 +1,32 @@ @@ -151,11 +153,11 @@ index 0000000..8b3a74e +# Add tacplus to /etc/nsswitch.conf, since it's necessary +# for this package, and won't break anything else. Do nothing +# if tacplus is already present in the passwd line -+if [ -e "/etc/nsswitch.conf" ]; then -+ sed -i -e '/tacplus/b' \ -+ -e '/^passwd/s/compat/& tacplus/' /etc/nsswitch.conf -+fi -+ ++#if [ -e "/etc/nsswitch.conf" ]; then ++# sed -i -e '/tacplus/b' \ ++# -e '/^passwd/s/compat/& tacplus/' /etc/nsswitch.conf ++#fi ++# Only when tacacs enable, tacplus is inserted in nsswitch.conf + +#DEBHELPER# + @@ -169,26 +171,29 @@ index 0000000..f476e7d +libnss_tacplus.so.2 libnss-tacplus #MINVER# + _nss_tacplus_getpwnam_r@Base 1.0.1 diff --git a/debian/rules b/debian/rules -index 0fa1f54..c0251b8 100755 +index 0fa1f54..363343e 100755 --- a/debian/rules +++ b/debian/rules @@ -22,5 +22,7 @@ override_dh_auto_configure: override_dh_install: mkdir -p debian/libpam-tacplus/usr/share/pam-configs cp debian/tacplus debian/libpam-tacplus/usr/share/pam-configs/ -+ # mkdir -p debian/libnss-tacplus/etc -+ # cp debian/tmp/etc/tacplus_nss.conf debian/libnss-tacplus/etc/ ++ mkdir -p debian/libnss-tacplus/etc ++ cp debian/tmp/etc/tacplus_nss.conf debian/libnss-tacplus/etc/ dh_install diff --git a/nss_tacplus.c b/nss_tacplus.c new file mode 100644 -index 0000000..3438db9 +index 0000000..1bf8dee --- /dev/null +++ b/nss_tacplus.c -@@ -0,0 +1,598 @@ +@@ -0,0 +1,838 @@ +/* -+ * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved. ++ * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. ++ * Copyright (C) 2017 Chenchen Qi ++ * All rights reserved. + * Author: Dave Olson ++ * Chenchen Qi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by @@ -205,12 +210,7 @@ index 0000000..3438db9 + */ + +/* -+ * This plugin implements getpwnam_r for NSS over TACACS+ -+ * and implements getpwuid_r for UIDs if and only if a mapped -+ * TACACS+ user is currently logged in (libtacplus_map) -+ * This means that if you do, e.g.: ls -ld ~tacacs15, you will -+ * sometimes get a mapped username, and other times get tacacs15, -+ * depending on whether a mapped user is logged in or not. ++ * This plugin implements getpwnam_r for NSS over TACACS+. + */ + +#include @@ -226,8 +226,13 @@ index 0000000..3438db9 + +#include + ++#define MIN_TACACS_USER_PRIV (1) ++#define MAX_TACACS_USER_PRIV (15) ++ +static const char *nssname = "nss_tacplus"; /* for syslogs */ +static const char *config_file = "/etc/tacplus_nss.conf"; ++static const char *user_conf = "/etc/tacplus_user"; ++static const char *user_conf_tmp = "/etc/tacplus_user_tmp"; + +/* + * pwbuf is used to reduce number of arguments passed around; the strings in @@ -245,69 +250,35 @@ index 0000000..3438db9 + struct addrinfo *addr; + char *key; + int timeout; -+} tacplus_server_t; ++}tacplus_server_t; + -+enum tac_user { -+ TAC_USER_SU = 0, -+ TAC_USER = 1, -+ TAC_USER_NUM -+}; ++typedef struct { ++ char *info; ++ int gid; ++ char *secondary_grp; ++ char *shell; ++}useradd_info_t; + +/* set from configuration file parsing */ +static tacplus_server_t tac_srv[TAC_PLUS_MAXSERVERS]; +static int tac_srv_no; -+static struct addrinfo *source_addr = NULL; -+static struct passwd tac_pw[TAC_USER_NUM]; -+const char *tac_pw_name[TAC_USER_NUM] = {"remote_user_su", "remote_user"}; -+ -+static char tac_service[] = "shell"; -+static char tac_protocol[] = "ssh"; -+static int debug=1; -+static int conf_parsed = 0; -+ -+static int parse_tac_pw() -+{ -+ FILE *fp; -+ struct passwd *pw; -+ int index, count = 0; -+ -+ fp = fopen("/etc/passwd", "r"); -+ if(NULL == fp) -+ { -+ syslog(LOG_ERR, "%s: /etc/passwd fopen failed", nssname); -+ return NSS_STATUS_UNAVAIL; -+ } -+ -+ while(0 != (pw = fgetpwent(fp))) -+ { -+ for(index = TAC_USER_SU; index < TAC_USER_NUM; ++index) -+ { -+ if(!strcmp(pw->pw_name, tac_pw_name[index])) -+ { -+ tac_pw[index].pw_name = strdup(pw->pw_name); -+ tac_pw[index].pw_passwd = strdup(pw->pw_passwd); -+ tac_pw[index].pw_gecos = strdup(pw->pw_gecos); -+ tac_pw[index].pw_dir = strdup(pw->pw_dir); -+ tac_pw[index].pw_shell = strdup(pw->pw_shell); -+ tac_pw[index].pw_uid = pw->pw_uid; -+ tac_pw[index].pw_gid = pw->pw_gid; -+ ++count; -+ if(debug) -+ syslog(LOG_DEBUG, "%s: tac_pw name %s", nssname, -+ tac_pw[index].pw_name); -+ } -+ } -+ } -+ fclose(fp); -+ -+ if(count < TAC_USER_NUM) -+ { -+ syslog(LOG_ERR, "%s: tacacs user passwd parsed %d < %d", nssname, -+ count, TAC_USER_NUM); -+ } -+ -+ return 0; -+} ++static struct addrinfo *source_addr; ++/* useradd_grp_list[0] is not used, it is convenient for use privilege ++ * value as array index directly */ ++static useradd_info_t useradd_grp_list[MAX_TACACS_USER_PRIV + 1]; ++ ++static char *tac_service = "shell"; ++static char *tac_protocol = "ssh"; ++static bool debug = false; ++static bool many_to_one = false; ++ ++/* default useradd info */ ++static char *useradd_sudo_info = "remote_user_su"; ++static char *useradd_sudo_grp = "sudo,docker"; ++static char *useradd_sudo_shell = "/bin/bash"; ++static char *useradd_info = "remote_user"; ++static char *useradd_grp = "docker"; ++static char *useradd_shell = "/bin/bash"; + +static int parse_tac_server(char *srv_buf) +{ @@ -315,12 +286,9 @@ index 0000000..3438db9 + char delim[] = " ,\t\n\r\f"; + + token = strsep(&srv_buf, delim); -+ while(NULL != token) -+ { -+ if('\0' != token) -+ { -+ if(!strncmp(token, "server=", 7)) -+ { ++ while(token) { ++ if('\0' != token) { ++ if(!strncmp(token, "server=", 7)) { + struct addrinfo hints, *server; + int rv; + char *srv, *port; @@ -328,53 +296,48 @@ index 0000000..3438db9 + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; -+ ++ + srv = token + 7; + port = strchr(srv, ':'); -+ if(NULL != port) -+ { ++ if(port) { + *port = '\0'; + port++; + } + + if((rv = getaddrinfo(srv, (port == NULL) ? "49" : port, &hints, -+ &server)) == 0) -+ { -+ if(NULL != server) -+ { -+ if(NULL != tac_srv[tac_srv_no].addr) ++ &server)) == 0) { ++ if(server) { ++ if(tac_srv[tac_srv_no].addr) + freeaddrinfo(tac_srv[tac_srv_no].addr); -+ if(NULL != tac_srv[tac_srv_no].key) ++ if(tac_srv[tac_srv_no].key) + free(tac_srv[tac_srv_no].key); + memset(tac_srv + tac_srv_no, 0, sizeof(tacplus_server_t)); + + tac_srv[tac_srv_no].addr = server; + } -+ else -+ { ++ else { + syslog(LOG_ERR, "%s: server NULL", nssname); + } + } -+ else -+ { ++ else { + syslog(LOG_ERR, "%s: invalid server: %s (getaddrinfo: %s)", + nssname, srv, gai_strerror(rv)); + return -1; + } + } -+ else if(!strncmp(token, "secret=", 7)) -+ { ++ else if(!strncmp(token, "secret=", 7)) { ++ if(tac_srv[tac_srv_no].key) ++ free(tac_srv[tac_srv_no].key); + tac_srv[tac_srv_no].key = strdup(token + 7); + } -+ else if(!strncmp(token, "timeout=", 8)) -+ { ++ else if(!strncmp(token, "timeout=", 8)) { + tac_srv[tac_srv_no].timeout = (int)strtoul(token + 8, NULL, 0); + if(tac_srv[tac_srv_no].timeout < 0) + tac_srv[tac_srv_no].timeout = 0; + /* Limit timeout to make sure upper application not wait + * for a long time*/ -+ if(tac_srv[tac_srv_no].timeout > 3) -+ tac_srv[tac_srv_no].timeout = 3; ++ if(tac_srv[tac_srv_no].timeout > 5) ++ tac_srv[tac_srv_no].timeout = 5; + } + } + token = strsep(&srv_buf, delim); @@ -383,38 +346,112 @@ index 0000000..3438db9 + return 0; +} + ++static int parse_user_priv(char *buf) ++{ ++ char *token; ++ char delim[] = ";\n\r"; ++ int priv = 0; ++ int gid = 0; ++ char *info = NULL; ++ char *group = NULL; ++ char *shell = NULL; ++ ++ token = strsep(&buf, delim); ++ while(token) { ++ if('\0' != token) { ++ if(!strncmp(token, "user_priv=", 10)) { ++ priv = (int)strtoul(token + 10, NULL, 0); ++ if(priv > MAX_TACACS_USER_PRIV || priv < MIN_TACACS_USER_PRIV) ++ syslog(LOG_WARNING, "%s: user_priv %d out of range", ++ nssname, priv); ++ } ++ else if(!strncmp(token, "pw_info=", 8)) { ++ if(!info) ++ info = strdup(token + 8); ++ } ++ else if(!strncmp(token, "gid=", 4)) { ++ gid = (int)strtoul(token + 4, NULL, 0); ++ } ++ else if(!strncmp(token, "group=", 6)) { ++ if(!group) ++ group = strdup(token + 6); ++ } ++ else if(!strncmp(token, "shell=", 6)) { ++ if(!shell) ++ shell = strdup(token + 6); ++ } ++ } ++ token = strsep(&buf, delim); ++ } ++ ++ if(priv && gid && info && group && shell) { ++ useradd_info_t *user = &useradd_grp_list[priv]; ++ if(user->info) ++ free(user->info); ++ if(user->secondary_grp) ++ free(user->secondary_grp); ++ if(user->shell) ++ free(user->shell); ++ ++ user->gid = gid; ++ user->info = info; ++ user->secondary_grp = group; ++ user->shell = shell; ++ syslog(LOG_DEBUG, "%s: user_priv=%d info=%s gid=%d group=%s shell=%s", ++ nssname, priv, info, gid, group, shell); ++ } ++ else { ++ if(info) ++ free(info); ++ if(group) ++ free(group); ++ if(shell) ++ free(shell); ++ } ++ ++ return 0; ++} ++ +static int parse_config(const char *file) +{ + FILE *fp; + char buf[512] = {0}; ++ useradd_info_t *user; + -+ if(conf_parsed < 1) -+ { -+ if(0 == parse_tac_pw()) -+ conf_parsed = 1; -+ else -+ return NSS_STATUS_UNAVAIL; -+ } ++ user = &useradd_grp_list[MAX_TACACS_USER_PRIV]; ++ user->gid = 1000; ++ user->info = useradd_sudo_info; ++ user->secondary_grp = useradd_sudo_grp; ++ user->shell = useradd_sudo_shell; ++ ++ user = &useradd_grp_list[MIN_TACACS_USER_PRIV]; ++ user->gid = 999; ++ user->info = useradd_info; ++ user->secondary_grp = useradd_grp; ++ user->shell = useradd_shell; + + fp = fopen(file, "r"); -+ if(NULL == fp) -+ { ++ if(!fp) { + syslog(LOG_ERR, "%s: %s fopen failed", nssname, file); + return NSS_STATUS_UNAVAIL; + } + ++ debug = false; + tac_srv_no = 0; -+ while(fgets(buf, sizeof(buf), fp)) -+ { ++ while(fgets(buf, sizeof buf, fp)) { + if('#' == *buf || isspace(*buf)) + continue; + -+ if(!strncmp(buf, "debug=", 6)) -+ { -+ debug = strtoul(buf + 6, NULL, 0); ++ if(!strncmp(buf, "debug=on", 8)) { ++ debug = true; ++ } ++ else if(!strncmp(buf, "many_to_one=y", 13)) { ++ many_to_one = true; ++ } ++ else if(!strncmp(buf, "user_priv=", 10)) { ++ parse_user_priv(buf); + } -+ else if(!strncmp(buf, "src_ip=", 7)) -+ { ++ else if(!strncmp(buf, "src_ip=", 7)) { + struct addrinfo hints; + char *ip = buf + 7; + @@ -429,39 +466,43 @@ index 0000000..3438db9 + syslog(LOG_ERR, "%s: error setting the source ip information", + nssname); + } -+ else if(!strncmp(buf, "server=", 7)) -+ { -+ if(TAC_PLUS_MAXSERVERS > tac_srv_no) -+ { -+ if(0 == parse_tac_server(buf)) -+ ++tac_srv_no; -+ } -+ else ++ else if(!strncmp(buf, "server=", 7)) { ++ if(TAC_PLUS_MAXSERVERS <= tac_srv_no) { + syslog(LOG_ERR, "%s: tac server num is more than %d", + nssname, TAC_PLUS_MAXSERVERS); ++ } ++ else if(0 == parse_tac_server(buf)) ++ ++tac_srv_no; + } + } + fclose(fp); + -+ if(debug) -+ { ++ if(debug) { + int n; + -+ for(n = 0; n < tac_srv_no; ++n) -+ { ++ for(n = 0; n < tac_srv_no; n++) { + syslog(LOG_DEBUG, "%s: server[%d] { addr=%s, key=%s, timeout=%d }", + nssname, n, tac_ntop(tac_srv[n].addr->ai_addr), + tac_srv[n].key, tac_srv[n].timeout); + } -+ + syslog(LOG_DEBUG, "%s: src_ip=%s", nssname, NULL == source_addr + ? "NULL" : tac_ntop(source_addr->ai_addr)); ++ syslog(LOG_DEBUG, "%s: many_to_one %s", nssname, 1 == many_to_one ++ ? "enable" : "disable"); ++ for(n = MIN_TACACS_USER_PRIV; n <= MAX_TACACS_USER_PRIV; n++) { ++ user = &useradd_grp_list[n]; ++ if(user) { ++ syslog(LOG_DEBUG, "%s: user_priv[%d] { gid=%d, info=%s, group=%s, shell=%s }", ++ nssname, n, user->gid, NULL == user->info ? "NULL" : user->info, ++ NULL == user->secondary_grp ? "NULL" : user->secondary_grp, ++ NULL == user->shell ? "NULL" : user->shell); ++ } ++ } + } + + return 0; +} + -+ +/* + * copy a passwd structure and it's strings, using the provided buffer + * for the strings. @@ -500,8 +541,8 @@ index 0000000..3438db9 + cnt++; /* allow for null byte also */ + buf += cnt; + len -= cnt; -+ /* set pw_passwd "a" for pam_account success */ -+ cnt = snprintf(buf, len, "%s", "a"); ++ /* If many-to-one mapping, set pw_passwd "a" for pam_account success */ ++ cnt = snprintf(buf, len, "%s", 0 == many_to_one ? "x" : "a"); + destpw->pw_passwd = buf; + cnt++; + buf += cnt; @@ -526,6 +567,222 @@ index 0000000..3438db9 +} + +/* ++ * If useradd finished, user name should be deleted in conf. ++ */ ++static int delete_conf_line(const char *name) ++{ ++ FILE *fp, *fp_tmp; ++ char line[128]; ++ char del_line[128]; ++ int len = strlen(name); ++ ++ if(len >= 126) { ++ syslog(LOG_ERR, "%s: user name %s out of range 128", nssname, name); ++ return -1; ++ } ++ else { ++ snprintf(del_line, 128, "%s\n", name); ++ } ++ ++ fp = fopen(user_conf, "r"); ++ if(!fp) { ++ syslog(LOG_ERR, "%s: %s fopen failed", nssname, user_conf); ++ return NSS_STATUS_UNAVAIL; ++ } ++ fp_tmp = fopen(user_conf_tmp, "w"); ++ if(!fp_tmp) { ++ syslog(LOG_ERR, "%s: %s fopen failed", nssname, user_conf_tmp); ++ fclose(fp); ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ while(fgets(line, sizeof line, fp)) { ++ if(strcmp(line, del_line)) { ++ fprintf(fp_tmp, "%s", line); ++ } ++ } ++ fclose(fp_tmp); ++ fclose(fp); ++ ++ if(0 != remove(user_conf) || 0 != rename(user_conf_tmp, user_conf)) { ++ syslog(LOG_ERR, "%s: %s rewrite failed", nssname, user_conf); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * If not found in local, look up in tacacs user conf. If user name is not in ++ * conf, it will be written in conf and created by command 'useradd'. When ++ * useradd command use getpwnam(), it will return when username found in conf. ++ */ ++static int create_local_user(const char *name, int level) ++{ ++ FILE *fp; ++ useradd_info_t *user; ++ char buf[512]; ++ int len = 512; ++ int lvl, cnt; ++ bool found = false; ++ ++ fp = fopen(user_conf, "ab+"); ++ if(!fp) { ++ syslog(LOG_ERR, "%s: %s fopen failed", nssname, user_conf); ++ return -1; ++ } ++ ++ while(fgets(buf, sizeof buf, fp)) { ++ if('#' == *buf || isspace(*buf)) ++ continue; ++ // Delete line break ++ cnt = strlen(buf); ++ buf[cnt - 1] = '\0'; ++ if(!strcmp(buf, name)) { ++ found = true; ++ break; ++ } ++ } ++ ++ /* ++ * If user is found in user_conf, it means that getpwnam is called by ++ * useradd in this NSS module. ++ */ ++ if(found) { ++ if(debug) ++ syslog(LOG_DEBUG, "%s: %s found in %s", nssname, name, user_conf); ++ fclose(fp); ++ return 1; ++ } ++ ++ snprintf(buf, len, "%s\n", name); ++ if(EOF == fputs(buf, fp)) { ++ syslog(LOG_ERR, "%s: %s write local user failed", nssname, name); ++ fclose(fp); ++ return -1; ++ } ++ fclose(fp); ++ ++ lvl = level; ++ while(lvl >= MIN_TACACS_USER_PRIV) { ++ user = &useradd_grp_list[lvl]; ++ if(user->info && user->secondary_grp && user->shell) { ++ snprintf(buf, len, "useradd -G %s \"%s\" -g %d -c \"%s\" -d /home/%s -m -s %s", ++ user->secondary_grp, name, user->gid, user->info, name, user->shell); ++ fp = popen(buf, "r"); ++ if(!fp || -1 == pclose(fp)) { ++ syslog(LOG_ERR, "%s: useradd popen failed errno=%d %s", ++ nssname, errno, strerror(errno)); ++ delete_conf_line(name); ++ return -1; ++ } ++ if(debug) ++ syslog(LOG_DEBUG, "%s: create local user %s success", nssname, name); ++ delete_conf_line(name); ++ return 0; ++ } ++ lvl--; ++ } ++ ++ return -1; ++} ++ ++/* ++ * Lookup user in /etc/passwd, and fill up passwd info if found. ++ */ ++static int lookup_pw_local(char* username, struct pwbuf *pb, bool *found) ++{ ++ FILE *fp; ++ struct passwd *pw = NULL; ++ int ret = 0; ++ ++ if(!username) { ++ syslog(LOG_ERR, "%s: username invalid in check passwd", nssname); ++ return -1; ++ } ++ ++ fp = fopen("/etc/passwd", "r"); ++ if(!fp) { ++ syslog(LOG_ERR, "%s: /etc/passwd fopen failed", nssname); ++ return -1; ++ } ++ ++ while(0 != (pw = fgetpwent(fp))) { ++ if(!strcmp(pw->pw_name, username)) { ++ *found = true; ++ ret = pwcopy(pb->buf, pb->buflen, pw, pb->pw, username); ++ if(ret) ++ *pb->errnop = ERANGE; ++ break; ++ } ++ } ++ fclose(fp); ++ return ret; ++} ++ ++/* ++ * Lookup local user passwd info for TACACS+ user. If not found, local user will ++ * be created by user mapping strategy. ++ */ ++static int lookup_user_pw(struct pwbuf *pb, int level) ++{ ++ char *username = NULL; ++ char buf[128]; ++ int len = 128; ++ bool found = false; ++ int ret = 0; ++ ++ if(level < MIN_TACACS_USER_PRIV || level > MAX_TACACS_USER_PRIV) { ++ syslog(LOG_ERR, "%s: TACACS+ user %s privilege %d invalid", nssname, pb->name, level); ++ return -1; ++ } ++ ++ /* ++ * If many-to-one user mapping disable, create local user for each TACACS+ user ++ * The username of local user and TACACS+ user is the same. If many-to-one enable, ++ * look up the mapped local user name and passwd info. ++ */ ++ if(0 == many_to_one) { ++ username = pb->name; ++ } ++ else { ++ int lvl = level; ++ useradd_info_t *user; ++ ++ while(lvl >= MIN_TACACS_USER_PRIV) { ++ user = &useradd_grp_list[lvl]; ++ if(user->info && user->secondary_grp && user->shell) { ++ snprintf(buf, len, "%s", user->info); ++ username = buf; ++ if(debug) ++ syslog(LOG_DEBUG, "%s: %s mapping local user %s", nssname, ++ pb->name, username); ++ break; ++ } ++ lvl--; ++ } ++ } ++ ++ ret = lookup_pw_local(username, pb, &found); ++ if(debug) ++ syslog(LOG_DEBUG, "%s: %s passwd %s found in local", nssname, username, ++ found ? "is" : "isn't"); ++ if(0 != ret || found) ++ return ret; ++ ++ if(0 != create_local_user(username, level)) ++ return -1; ++ ++ ret = lookup_pw_local(username, pb, &found); ++ if(0 == ret && !found) { ++ syslog(LOG_ERR, "%s: %s not found in local after useradd", nssname, pb->name); ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++/* + * we got the user back. Go through the attributes, find their privilege + * level, map to the local user, fill in the data, etc. + * Returns 0 on success, 1 on errors. @@ -560,26 +817,12 @@ index 0000000..3438db9 + attr = attr->next; + } + -+ if(priv_level == 15) -+ /* remote_user_su has sudo and docker privilege. If it return login -+ * username, user only get gid, can't get group info in /etc/group. -+ * remote_user_su can use all command, and not need to command -+ * authorization by default, so not return login name is ok. -+ * remote_user only has docker privilege, its gid is created as -+ * docker group id. -+ * */ -+ ret = pwcopy(pb->buf, pb->buflen, tac_pw + TAC_USER_SU, pb->pw, NULL); -+ else -+ ret = pwcopy(pb->buf, pb->buflen, tac_pw + TAC_USER, pb->pw, pb->name); -+ -+ if(debug) ++ ret = lookup_user_pw(pb, priv_level); ++ if(!ret && debug) + syslog(LOG_DEBUG, "%s: pw_name=%s, pw_passwd=%s, pw_shell=%s, dir=%s", + nssname, pb->pw->pw_name, pb->pw->pw_passwd, pb->pw->pw_shell, + pb->pw->pw_dir); + -+ if(ret) -+ *pb->errnop = ERANGE; -+ + return ret; +} + @@ -695,48 +938,44 @@ index 0000000..3438db9 + +static bool is_valid_name (const char *name) +{ -+ /* -+ * User/group names must match [a-z_][a-z0-9_-]*[$] -+ */ -+ if (('\0' == *name) || -+ !((('a' <= *name) && ('z' >= *name)) || ('_' == *name))) { -+ return false; -+ } -+ -+ while ('\0' != *++name) { -+ if (!(( ('a' <= *name) && ('z' >= *name) ) || -+ ( ('0' <= *name) && ('9' >= *name) ) || -+ ('_' == *name) || -+ ('-' == *name) || -+ ( ('$' == *name) && ('\0' == *(name + 1)) ) -+ )) { -+ return false; -+ } -+ } -+ -+ return true; ++ /* ++ * User/group names must match [a-z_][a-z0-9_-]*[$] ++ */ ++ if(('\0' == *name) || ++ !((('a' <= *name) && ('z' >= *name)) || ('_' == *name))) { ++ return false; ++ } ++ ++ while('\0' != *++name) { ++ if(!(( ('a' <= *name) && ('z' >= *name) ) || ++ ( ('0' <= *name) && ('9' >= *name) ) || ++ ('_' == *name) || ++ ('-' == *name) || ++ ( ('$' == *name) && ('\0' == *(name + 1)) ) ++ )) { ++ return false; ++ } ++ } ++ ++ return true; +} + +static bool is_valid_user_name (const char *name) +{ -+ /* -+ * User names are limited by whatever utmp can -+ * handle. -+ */ -+ if (strlen (name) > 32) { -+ return false; -+ } -+ -+ return is_valid_name (name); ++ /* ++ * User names are limited by whatever utmp can ++ * handle. ++ */ ++ if(strlen (name) > 32) { ++ return false; ++ } ++ ++ return is_valid_name (name); +} + +/* + * This is an NSS entry point. -+ * We implement getpwnam(), because we remap from the tacacs login -+ * to the local tacacs0 ... tacacs15 users for all other info, and so -+ * the normal order of "passwd tacplus" (possibly with ldap or anything -+ * else prior to tacplus) will mean we only get used when there isn't -+ * a local user to be found. ++ * We implement getpwnam(), because we remap from the tacacs. + * + * We try the lookup to the tacacs server first. If we can't make a + * connection to the server for some reason, we also try looking up @@ -751,18 +990,22 @@ index 0000000..3438db9 + int result; + struct pwbuf pbuf; + -+ /* When filename completion is used with the tab key in bash, getpwnam -+ is invoked. And the parameter "name" is '*'. In order not to connect to -+ TACACS+ server frequently, check user name whether is valid. */ ++ /* ++ * When filename completion is used with the tab key in bash, getpwnam ++ * is invoked. And the parameter "name" is '*'. In order not to connect to ++ * TACACS+ server frequently, check user name whether is valid. ++ */ + if(!is_valid_user_name(name)) + return NSS_STATUS_NOTFOUND; + + result = parse_config(config_file); + -+ if(result) { /* no config file, no servers, etc. */ -+ /* this is a debug because privileges may not allow access */ -+ if(debug) -+ syslog(LOG_DEBUG, "%s: bad config or server line for nss_tacplus", ++ if(result) { ++ syslog(LOG_ERR, "%s: bad config or server line for nss_tacplus", ++ nssname); ++ } ++ else if(0 == tac_srv_no) { ++ syslog(LOG_WARNING, "%s: no tacacs server in config for nss_tacplus", + nssname); + } + else { @@ -773,24 +1016,72 @@ index 0000000..3438db9 + pbuf.buflen = buflen; + pbuf.errnop = errnop; + -+ if(0 == lookup_tacacs_user(&pbuf)) ++ if(0 == lookup_tacacs_user(&pbuf)) { + status = NSS_STATUS_SUCCESS; -+ } -+ -+ if(debug) -+ syslog(LOG_DEBUG, "%s: name=%s, pw_name=%s, pw_passwd=%s, pw_shell=%s", ++ if(debug) ++ syslog(LOG_DEBUG, "%s: name=%s, pw_name=%s, pw_passwd=%s, pw_shell=%s", + nssname, name, pw->pw_name, pw->pw_passwd, pw->pw_shell); ++ } ++ } + + return status; +} -+ diff --git a/tacplus_nss.conf b/tacplus_nss.conf new file mode 100644 -index 0000000..e22794c +index 0000000..7cb756f --- /dev/null +++ b/tacplus_nss.conf -@@ -0,0 +1 @@ -+debug=1 +@@ -0,0 +1,50 @@ ++# Configuration for libnss-tacplus ++ ++# debug - If you want to open debug log, set it on ++# ++# Default: off ++# debug=on ++ ++# src_ip - set source address of TACACS+ protocl packets ++# ++# Default: None (it means the ip address of out port) ++# src_ip=2.2.2.2 ++ ++# server - set ip address, tcp port, secret string and timeout for TACACS+ servers ++# The maximum number of servers is 8. If there is no TACACS+ server, libnss-tacplus ++# will always return pwname not found. ++# ++# 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 ++# If TACACS+ user validate ok, it will get passwd info from local user which is ++# specially created for TACACS+ user in libnss-tacplus. This configuration is provided ++# to create local user. There is two user privilege map by default. ++# If the TACACS+ user's privilege value is in [1, 14], the config of user_priv 1 is ++# used to create local user. If user_priv 7 is added, the TACACS+ user which privilege ++# value is in [1, 6] will get the config of user_priv 1, and the value in [7, 14] will ++# get user_priv 7. ++# ++# If the passwd info of mapped local user is modified, like gid and shell, the new TACACS+ ++# user will create local user by the new config. But the old TACACS+ user which has logged ++# will not modify its mapped local user's passwd info. So it's better to keep this ++# configuration unchanged, not to modified at the running time. Or simply delete the old ++# mapped local user after modified. ++# ++# NOTE: If many_to_one enables, 'pw_info' is used for mapped local user name. So note the ++# naming rule for Linux user name when you set 'pw_info', and keep it different from other ++# 'pw_info'. ++# ++# 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 ++# The parameter 'pw_info' in 'user_priv' is used for the mapped local user name. ++# The default config is one to one mapping. It will create local user for each TACACS+ user ++# which has different username. The user mapping strategy should be set before enables ++# TACACS+, and keep constant at the running time. ++# ++# Default: many_to_one=n ++# many_to_one=y -- 2.7.4 From b2ae6924a508c1a7d1383b02a9c3fb99d7fe3a2b Mon Sep 17 00:00:00 2001 From: Liuqu Date: Mon, 21 Aug 2017 05:33:03 -0700 Subject: [PATCH 7/7] [TACACS+]: Fix libpam_tacplus build error * Delete build dependence 'autoconf-archive' --- sonic-slave/Dockerfile | 2 -- src/tacacs/0002-Add-nss-tacplus.patch | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/sonic-slave/Dockerfile b/sonic-slave/Dockerfile index efa8fab90d32..826a7129a7e0 100644 --- a/sonic-slave/Dockerfile +++ b/sonic-slave/Dockerfile @@ -198,8 +198,6 @@ RUN apt-get update && apt-get install -y \ python-yaml \ # For lockfile procmail \ -# For pam-tacplus build - autoconf-archive \ # For gtest libgtest-dev \ cmake diff --git a/src/tacacs/0002-Add-nss-tacplus.patch b/src/tacacs/0002-Add-nss-tacplus.patch index d915b99a5efc..64e545cccf86 100644 --- a/src/tacacs/0002-Add-nss-tacplus.patch +++ b/src/tacacs/0002-Add-nss-tacplus.patch @@ -1,4 +1,4 @@ -From 3a293e757232aa801386b671c21f25cdd14cacbd Mon Sep 17 00:00:00 2001 +From b8ed986b2ee17c0d3253a685a8ac2e4850a2a159 Mon Sep 17 00:00:00 2001 From: Liuqu Date: Wed, 16 Aug 2017 06:24:45 -0700 Subject: [PATCH 2/2] Add nss-tacplus @@ -9,7 +9,7 @@ Subject: [PATCH 2/2] Add nss-tacplus --- Makefile.am | 36 ++ configure.ac | 1 + - debian/control | 6 + + debian/control | 8 +- debian/libnss-tacplus.install | 2 + debian/libnss-tacplus.lintian-overrides | 8 + debian/libnss-tacplus.postinst | 32 ++ @@ -17,7 +17,7 @@ Subject: [PATCH 2/2] Add nss-tacplus debian/rules | 2 + nss_tacplus.c | 838 ++++++++++++++++++++++++++++++++ tacplus_nss.conf | 50 ++ - 10 files changed, 977 insertions(+) + 10 files changed, 978 insertions(+), 1 deletion(-) create mode 100644 debian/libnss-tacplus.install create mode 100644 debian/libnss-tacplus.lintian-overrides create mode 100644 debian/libnss-tacplus.postinst @@ -89,9 +89,18 @@ index e34c769..bd683a8 100644 AM_CONDITIONAL(MY_MD5, [test "$ac_cv_header_openssl_md5_h" = "no" ]) AM_CONDITIONAL(TACC, [test "$ac_cv_lib_crypto_RAND_pseudo_bytes" = "yes"]) diff --git a/debian/control b/debian/control -index 2e851b1..1e0b25d 100644 +index 2e851b1..7e914e3 100644 --- a/debian/control +++ b/debian/control +@@ -2,7 +2,7 @@ Source: libpam-tacplus + Section: admin + Priority: extra + Maintainer: Jeroen Nijhof +-Build-Depends: debhelper (>= 9), libpam-dev, dh-autoreconf, autoconf-archive ++Build-Depends: debhelper (>= 9), libpam-dev, dh-autoreconf + Standards-Version: 3.9.5 + Homepage: https://github.com/jeroennijhof/pam_tacplus + @@ -34,3 +34,9 @@ Depends: ${misc:Depends}, libtac2 (= ${binary:Version}), libc6-dev|libc-dev Description: Development files for TACACS+ protocol library Contains C header files and development files for libtac, a TACACS+ protocol