diff --git a/files/image_config/hostcfgd/common-auth-sonic.j2 b/files/image_config/hostcfgd/common-auth-sonic.j2 index e609296d4e05..a9e95fda19fd 100644 --- a/files/image_config/hostcfgd/common-auth-sonic.j2 +++ b/files/image_config/hostcfgd/common-auth-sonic.j2 @@ -15,16 +15,16 @@ auth [success=1 default=ignore] pam_unix.so nullok try_first_pass {% elif auth['login'] == 'local,tacacs+' %} auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass {% for server in servers | sub(0, -1) %} -auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {% if server.vrf %} vrf={{ server.vrf }} {% endif %} try_first_pass +auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {% if server.vrf %} vrf={{ server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass {% endfor %} {% if servers | count %} {% set last_server = servers | last %} -auth [success=1 default=ignore] pam_tacplus.so server={{ last_server.ip }}:{{ last_server.tcp_port }} secret={{ last_server.passkey }} login={{ last_server.auth_type }} timeout={{ last_server.timeout }} {% if last_server.vrf %} vrf={{ last_server.vrf }} {% endif %} try_first_pass +auth [success=1 default=ignore] pam_tacplus.so server={{ last_server.ip }}:{{ last_server.tcp_port }} secret={{ last_server.passkey }} login={{ last_server.auth_type }} timeout={{ last_server.timeout }} {% if last_server.vrf %} vrf={{ last_server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass {% endif %} {% elif auth['login'] == 'tacacs+' or auth['login'] == 'tacacs+,local' %} {% for server in servers %} -auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {%if server.vrf %} vrf={{ server.vrf }} {% endif %} try_first_pass +auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_tacplus.so server={{ server.ip }}:{{ server.tcp_port }} secret={{ server.passkey }} login={{ server.auth_type }} timeout={{ server.timeout }} {%if server.vrf %} vrf={{ server.vrf }} {% endif %} {{ 'source_ip=%s' % src_ip if src_ip }} try_first_pass {% endfor %} 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 b950345d3d9b..67d5126773f8 100755 --- a/files/image_config/hostcfgd/hostcfgd +++ b/files/image_config/hostcfgd/hostcfgd @@ -212,6 +212,10 @@ class AaaCfg(object): auth.update(self.auth) tacplus_global = self.tacplus_global_default.copy() tacplus_global.update(self.tacplus_global) + if 'src_ip' in tacplus_global: + src_ip = tacplus_global['src_ip'] + else: + src_ip = None servers_conf = [] if self.tacplus_servers: @@ -226,7 +230,7 @@ class AaaCfg(object): 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) + pam_conf = template.render(auth=auth, src_ip=src_ip, servers=servers_conf) with open(PAM_AUTH_CONF, 'w') as f: f.write(pam_conf) @@ -249,7 +253,7 @@ class AaaCfg(object): # Set tacacs+ server in nss-tacplus conf template_file = os.path.abspath(NSS_TACPLUS_CONF_TEMPLATE) template = env.get_template(template_file) - nss_tacplus_conf = template.render(debug=self.debug, servers=servers_conf) + nss_tacplus_conf = template.render(debug=self.debug, src_ip=src_ip, servers=servers_conf) with open(NSS_TACPLUS_CONF, 'w') as f: f.write(nss_tacplus_conf) diff --git a/files/image_config/hostcfgd/tacplus_nss.conf.j2 b/files/image_config/hostcfgd/tacplus_nss.conf.j2 index 7f737888f59d..8fad3035d601 100644 --- a/files/image_config/hostcfgd/tacplus_nss.conf.j2 +++ b/files/image_config/hostcfgd/tacplus_nss.conf.j2 @@ -7,6 +7,13 @@ debug=on {% endif %} +# src_ip - set source address of TACACS+ protocol packets +# Default: None (auto source ip address) +# src_ip=2.2.2.2 +{% if src_ip %} +src_ip={{ src_ip }} +{% endif %} + # server - set ip address, tcp port, secret string and timeout for TACACS+ servers # Default: None (no TACACS+ server) # server=1.1.1.1:49,secret=test,timeout=3 diff --git a/src/tacacs/nss/0007-Add-support-for-TACACS-source-address.patch b/src/tacacs/nss/0007-Add-support-for-TACACS-source-address.patch new file mode 100644 index 000000000000..251ef172b23e --- /dev/null +++ b/src/tacacs/nss/0007-Add-support-for-TACACS-source-address.patch @@ -0,0 +1,77 @@ +From 61e951efe54085fe427a32d0e7db8ef08c02fa95 Mon Sep 17 00:00:00 2001 +From: Venkatesan Mahalingam +Date: Mon, 6 Jul 2020 12:14:26 -0700 +Subject: [PATCH] Add support for TACACS+ source address. + +Signed-off-by: Venkatesan Mahalingam +--- + nss_tacplus.c | 25 ++++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/nss_tacplus.c b/nss_tacplus.c +index 64a9328..bf6b934 100644 +--- a/nss_tacplus.c ++++ b/nss_tacplus.c +@@ -73,6 +73,7 @@ typedef struct { + static tacplus_server_t tac_srv[TAC_PLUS_MAXSERVERS]; + static int tac_srv_no; + static useradd_info_t useradd_grp_list[MAX_TACACS_USER_PRIV + 1]; ++static struct addrinfo *source_addr; + + static char *tac_service = "shell"; + static char *tac_protocol = "ssh"; +@@ -247,6 +248,10 @@ static int parse_config(const char *file) + return NSS_STATUS_UNAVAIL; + } + ++ if(source_addr) { ++ freeaddrinfo(source_addr); ++ source_addr = NULL; ++ } + debug = false; + tac_srv_no = 0; + while(fgets(buf, sizeof buf, fp)) { +@@ -262,6 +267,22 @@ static int parse_config(const char *file) + else if(!strncmp(buf, "user_priv=", 10)) { + parse_user_priv(buf); + } ++ else if(!strncmp(buf, "src_ip=", 7)) { ++ struct addrinfo hints; ++ char *ip = buf + 7, *new_line; ++ ++ // Remove the new line character as getaddrinfo is not working for IPv6 address with '\n'. ++ if ((new_line = strchr(buf, '\n')) != NULL) { ++ *new_line = '\0'; ++ } ++ memset(&hints, 0, sizeof hints); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ ++ 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) { + syslog(LOG_ERR, "%s: tac server num is more than %d", +@@ -282,6 +303,8 @@ static int parse_config(const char *file) + nssname, n, tac_ntop(tac_srv[n].addr->ai_addr), + tac_srv[n].key[0], 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++) { +@@ -690,7 +713,7 @@ connect_tacacs(struct tac_attrib **attr, int srvr) + if(!*tac_service) /* reported at config file processing */ + return -1; + +- fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL, ++ fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, source_addr, + tac_srv[srvr].timeout, vrfname[0] ? vrfname : NULL); + if(fd >= 0) { + *attr = NULL; /* so tac_add_attr() allocates memory */ +-- +2.7.4 + diff --git a/src/tacacs/nss/Makefile b/src/tacacs/nss/Makefile index cb9382d07a89..c133cafe9212 100644 --- a/src/tacacs/nss/Makefile +++ b/src/tacacs/nss/Makefile @@ -24,6 +24,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : git $(GIT_APPLY) ../0004-Skip-accessing-tacacs-servers-for-local-non-tacacs-u.patch git $(GIT_APPLY) ../0005-libnss-Modify-parsing-of-IP-addr-and-port-number-str.patch git $(GIT_APPLY) ../0006-fix-compiling-warning-about-token-dereference.patch + git $(GIT_APPLY) ../0007-Add-support-for-TACACS-source-address.patch dpkg-buildpackage -rfakeroot -b -us -uc popd diff --git a/src/tacacs/pam/0006-Add-support-for-source-ip-address.patch b/src/tacacs/pam/0006-Add-support-for-source-ip-address.patch new file mode 100644 index 000000000000..280cfeee28fb --- /dev/null +++ b/src/tacacs/pam/0006-Add-support-for-source-ip-address.patch @@ -0,0 +1,131 @@ +From 9c26e734cf9e5cec950dc8b8f474f89d87833bcd Mon Sep 17 00:00:00 2001 +From: Venkatesan Mahalingam +Date: Wed, 1 Jul 2020 18:57:28 -0700 +Subject: [PATCH] Add support to specify source address for TACACS+ + +--- + pam_tacplus.c | 8 ++++---- + support.c | 31 +++++++++++++++++++++++++++++++ + support.h | 1 + + 3 files changed, 36 insertions(+), 4 deletions(-) + +diff --git a/pam_tacplus.c b/pam_tacplus.c +index 38e2a70..ec8ea27 100644 +--- a/pam_tacplus.c ++++ b/pam_tacplus.c +@@ -177,7 +177,7 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, + + status = PAM_SESSION_ERR; + for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { +- tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout, __vrfname); ++ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname); + if (tac_fd < 0) { + _pam_log(LOG_WARNING, "%s: error sending %s (fd)", + __FUNCTION__, typemsg); +@@ -276,7 +276,7 @@ int pam_sm_authenticate (pam_handle_t * pamh, int flags, + 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, __vrfname); ++ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname); + if (tac_fd < 0) { + _pam_log(LOG_ERR, "%s: connection to srv %d failed", __FUNCTION__, srv_i); + continue; +@@ -579,7 +579,7 @@ int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, + if(tac_protocol[0] != '\0') + tac_add_attrib(&attr, "protocol", tac_protocol); + +- tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL, tac_timeout, __vrfname); ++ tac_fd = tac_connect_single(active_server.addr, active_server.key, tac_source_addr, tac_timeout, __vrfname); + if(tac_fd < 0) { + _pam_log (LOG_ERR, "TACACS+ server unavailable"); + if(arep.msg != NULL) +@@ -762,7 +762,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, + 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, __vrfname); ++ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname); + 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 7c00618..3e55e2f 100644 +--- a/support.c ++++ b/support.c +@@ -37,6 +37,8 @@ char tac_service[64]; + char tac_protocol[64]; + char tac_prompt[64]; + char *__vrfname=NULL; ++char tac_source_ip[64]; ++struct addrinfo *tac_source_addr = NULL; + + void _pam_log(int err, const char *format,...) { + char msg[256]; +@@ -183,6 +185,12 @@ 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; ++ ++ if (tac_source_addr != NULL) { ++ freeaddrinfo(tac_source_addr); ++ tac_source_addr = NULL; ++ } + + for (ctrl = 0; argc-- > 0; ++argv) { + if (!strcmp (*argv, "debug")) { /* all */ +@@ -274,6 +282,10 @@ int _pam_parse (int argc, const char **argv) { + } + } else if(!strncmp(*argv, "vrf=", 4)) { + __vrfname = strdup(*argv + 4); ++ } 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)); ++ set_source_ip (tac_source_ip, &tac_source_addr); + } else { + _pam_log (LOG_WARNING, "unrecognized option: %s", *argv); + } +@@ -292,8 +304,27 @@ 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 */ ++ 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 9cbd040..09b8a85 100644 +--- a/support.h ++++ b/support.h +@@ -37,6 +37,7 @@ extern int tac_srv_no; + extern char tac_service[64]; + extern char tac_protocol[64]; + extern char tac_prompt[64]; ++extern struct addrinfo *tac_source_addr; + + int _pam_parse (int, const char **); + unsigned long _resolve_name (char *); +-- +2.7.4 + diff --git a/src/tacacs/pam/Makefile b/src/tacacs/pam/Makefile index 487cf975fd77..1f5213915641 100644 --- a/src/tacacs/pam/Makefile +++ b/src/tacacs/pam/Makefile @@ -19,6 +19,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : git apply ../0003-Obfuscate-key-before-printing-to-syslog.patch git apply ../0004-management-vrf-support.patch git apply ../0005-pam-Modify-parsing-of-IP-address-and-port-number-to-.patch + git apply ../0006-Add-support-for-source-ip-address.patch dpkg-buildpackage -rfakeroot -b -us -uc popd