Skip to content

Commit

Permalink
[TACACS+]: Add support to specify source address for TACACS+ (sonic-n…
Browse files Browse the repository at this point in the history
…et#4610)

This pull request was cherry picked from "sonic-net#1238" to resolve the conflicts.

- Why I did it
Add support to specify source address for TACACS+
- How I did it
Add patches for libpam-tacplus and libnss-tacplus. The patches parse the new option 'src_ip' and store the converted addrinfo. Then the addrinfo is used for TACACS+ connection.
Add a attribute 'src_ip' for table "TACPLUS|global" in configDB
Add some code to adapt to the attribute 'src_ip'.
- How to verify it
Config command for source address PR in sonic-utilities
config tacacs src_ip <ip_address>

- Description for the changelog
Add patches to specify source address for the TACACS+ outgoing packets.

- A picture of a cute animal (not mandatory but encouraged)

**UT logs: **

UT_tacacs_source_intf.txt
  • Loading branch information
venkatmahalingam authored and qiluo-msft committed Jul 12, 2020
1 parent 1dcf8ec commit 7d003c3
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 5 deletions.
6 changes: 3 additions & 3 deletions files/image_config/hostcfgd/common-auth-sonic.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 6 additions & 2 deletions files/image_config/hostcfgd/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)

Expand All @@ -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)

Expand Down
7 changes: 7 additions & 0 deletions files/image_config/hostcfgd/tacplus_nss.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
77 changes: 77 additions & 0 deletions src/tacacs/nss/0007-Add-support-for-TACACS-source-address.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
From 61e951efe54085fe427a32d0e7db8ef08c02fa95 Mon Sep 17 00:00:00 2001
From: Venkatesan Mahalingam <venkatesan_mahalinga@dell.com>
Date: Mon, 6 Jul 2020 12:14:26 -0700
Subject: [PATCH] Add support for TACACS+ source address.

Signed-off-by: Venkatesan Mahalingam <venkatesan_mahalinga@dell.com>
---
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

1 change: 1 addition & 0 deletions src/tacacs/nss/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
131 changes: 131 additions & 0 deletions src/tacacs/pam/0006-Add-support-for-source-ip-address.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
From 9c26e734cf9e5cec950dc8b8f474f89d87833bcd Mon Sep 17 00:00:00 2001
From: Venkatesan Mahalingam <venkatesan_mahalinga@dell.com>
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

1 change: 1 addition & 0 deletions src/tacacs/pam/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7d003c3

Please sign in to comment.