Skip to content

Commit ec0c826

Browse files
[TACACS+]: Add support to specify source address for TACACS+ (#4610)
This pull request was cherry picked from "#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
1 parent fa885c9 commit ec0c826

7 files changed

+226
-5
lines changed

files/image_config/hostcfgd/common-auth-sonic.j2

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
1515
{% elif auth['login'] == 'local,tacacs+' %}
1616
auth [success=done new_authtok_reqd=done default=ignore{{ ' auth_err=die' if not auth['failthrough'] }}] pam_unix.so nullok try_first_pass
1717
{% for server in servers | sub(0, -1) %}
18-
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
18+
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
1919
{% endfor %}
2020
{% if servers | count %}
2121
{% set last_server = servers | last %}
22-
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
22+
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
2323

2424
{% endif %}
2525
{% elif auth['login'] == 'tacacs+' or auth['login'] == 'tacacs+,local' %}
2626
{% for server in servers %}
27-
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
27+
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
2828
{% endfor %}
2929
auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
3030

files/image_config/hostcfgd/hostcfgd

+6-2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ class AaaCfg(object):
212212
auth.update(self.auth)
213213
tacplus_global = self.tacplus_global_default.copy()
214214
tacplus_global.update(self.tacplus_global)
215+
if 'src_ip' in tacplus_global:
216+
src_ip = tacplus_global['src_ip']
217+
else:
218+
src_ip = None
215219

216220
servers_conf = []
217221
if self.tacplus_servers:
@@ -226,7 +230,7 @@ class AaaCfg(object):
226230
env = jinja2.Environment(loader=jinja2.FileSystemLoader('/'), trim_blocks=True)
227231
env.filters['sub'] = sub
228232
template = env.get_template(template_file)
229-
pam_conf = template.render(auth=auth, servers=servers_conf)
233+
pam_conf = template.render(auth=auth, src_ip=src_ip, servers=servers_conf)
230234
with open(PAM_AUTH_CONF, 'w') as f:
231235
f.write(pam_conf)
232236

@@ -249,7 +253,7 @@ class AaaCfg(object):
249253
# Set tacacs+ server in nss-tacplus conf
250254
template_file = os.path.abspath(NSS_TACPLUS_CONF_TEMPLATE)
251255
template = env.get_template(template_file)
252-
nss_tacplus_conf = template.render(debug=self.debug, servers=servers_conf)
256+
nss_tacplus_conf = template.render(debug=self.debug, src_ip=src_ip, servers=servers_conf)
253257
with open(NSS_TACPLUS_CONF, 'w') as f:
254258
f.write(nss_tacplus_conf)
255259

files/image_config/hostcfgd/tacplus_nss.conf.j2

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
debug=on
88
{% endif %}
99

10+
# src_ip - set source address of TACACS+ protocol packets
11+
# Default: None (auto source ip address)
12+
# src_ip=2.2.2.2
13+
{% if src_ip %}
14+
src_ip={{ src_ip }}
15+
{% endif %}
16+
1017
# server - set ip address, tcp port, secret string and timeout for TACACS+ servers
1118
# Default: None (no TACACS+ server)
1219
# server=1.1.1.1:49,secret=test,timeout=3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
From 61e951efe54085fe427a32d0e7db8ef08c02fa95 Mon Sep 17 00:00:00 2001
2+
From: Venkatesan Mahalingam <[email protected]>
3+
Date: Mon, 6 Jul 2020 12:14:26 -0700
4+
Subject: [PATCH] Add support for TACACS+ source address.
5+
6+
Signed-off-by: Venkatesan Mahalingam <[email protected]>
7+
---
8+
nss_tacplus.c | 25 ++++++++++++++++++++++++-
9+
1 file changed, 24 insertions(+), 1 deletion(-)
10+
11+
diff --git a/nss_tacplus.c b/nss_tacplus.c
12+
index 64a9328..bf6b934 100644
13+
--- a/nss_tacplus.c
14+
+++ b/nss_tacplus.c
15+
@@ -73,6 +73,7 @@ typedef struct {
16+
static tacplus_server_t tac_srv[TAC_PLUS_MAXSERVERS];
17+
static int tac_srv_no;
18+
static useradd_info_t useradd_grp_list[MAX_TACACS_USER_PRIV + 1];
19+
+static struct addrinfo *source_addr;
20+
21+
static char *tac_service = "shell";
22+
static char *tac_protocol = "ssh";
23+
@@ -247,6 +248,10 @@ static int parse_config(const char *file)
24+
return NSS_STATUS_UNAVAIL;
25+
}
26+
27+
+ if(source_addr) {
28+
+ freeaddrinfo(source_addr);
29+
+ source_addr = NULL;
30+
+ }
31+
debug = false;
32+
tac_srv_no = 0;
33+
while(fgets(buf, sizeof buf, fp)) {
34+
@@ -262,6 +267,22 @@ static int parse_config(const char *file)
35+
else if(!strncmp(buf, "user_priv=", 10)) {
36+
parse_user_priv(buf);
37+
}
38+
+ else if(!strncmp(buf, "src_ip=", 7)) {
39+
+ struct addrinfo hints;
40+
+ char *ip = buf + 7, *new_line;
41+
+
42+
+ // Remove the new line character as getaddrinfo is not working for IPv6 address with '\n'.
43+
+ if ((new_line = strchr(buf, '\n')) != NULL) {
44+
+ *new_line = '\0';
45+
+ }
46+
+ memset(&hints, 0, sizeof hints);
47+
+ hints.ai_family = AF_UNSPEC;
48+
+ hints.ai_socktype = SOCK_STREAM;
49+
+
50+
+ if(0 != getaddrinfo(ip, NULL, &hints, &source_addr))
51+
+ syslog(LOG_ERR, "%s: error setting the source ip information",
52+
+ nssname);
53+
+ }
54+
else if(!strncmp(buf, "server=", 7)) {
55+
if(TAC_PLUS_MAXSERVERS <= tac_srv_no) {
56+
syslog(LOG_ERR, "%s: tac server num is more than %d",
57+
@@ -282,6 +303,8 @@ static int parse_config(const char *file)
58+
nssname, n, tac_ntop(tac_srv[n].addr->ai_addr),
59+
tac_srv[n].key[0], tac_srv[n].timeout);
60+
}
61+
+ syslog(LOG_DEBUG, "%s: src_ip=%s", nssname, NULL == source_addr
62+
+ ? "NULL" : tac_ntop(source_addr->ai_addr));
63+
syslog(LOG_DEBUG, "%s: many_to_one %s", nssname, 1 == many_to_one
64+
? "enable" : "disable");
65+
for(n = MIN_TACACS_USER_PRIV; n <= MAX_TACACS_USER_PRIV; n++) {
66+
@@ -690,7 +713,7 @@ connect_tacacs(struct tac_attrib **attr, int srvr)
67+
if(!*tac_service) /* reported at config file processing */
68+
return -1;
69+
70+
- fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL,
71+
+ fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, source_addr,
72+
tac_srv[srvr].timeout, vrfname[0] ? vrfname : NULL);
73+
if(fd >= 0) {
74+
*attr = NULL; /* so tac_add_attr() allocates memory */
75+
--
76+
2.7.4
77+

src/tacacs/nss/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
2424
git $(GIT_APPLY) ../0004-Skip-accessing-tacacs-servers-for-local-non-tacacs-u.patch
2525
git $(GIT_APPLY) ../0005-libnss-Modify-parsing-of-IP-addr-and-port-number-str.patch
2626
git $(GIT_APPLY) ../0006-fix-compiling-warning-about-token-dereference.patch
27+
git $(GIT_APPLY) ../0007-Add-support-for-TACACS-source-address.patch
2728

2829
dpkg-buildpackage -rfakeroot -b -us -uc
2930
popd
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
From 9c26e734cf9e5cec950dc8b8f474f89d87833bcd Mon Sep 17 00:00:00 2001
2+
From: Venkatesan Mahalingam <[email protected]>
3+
Date: Wed, 1 Jul 2020 18:57:28 -0700
4+
Subject: [PATCH] Add support to specify source address for TACACS+
5+
6+
---
7+
pam_tacplus.c | 8 ++++----
8+
support.c | 31 +++++++++++++++++++++++++++++++
9+
support.h | 1 +
10+
3 files changed, 36 insertions(+), 4 deletions(-)
11+
12+
diff --git a/pam_tacplus.c b/pam_tacplus.c
13+
index 38e2a70..ec8ea27 100644
14+
--- a/pam_tacplus.c
15+
+++ b/pam_tacplus.c
16+
@@ -177,7 +177,7 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv,
17+
18+
status = PAM_SESSION_ERR;
19+
for(srv_i = 0; srv_i < tac_srv_no; srv_i++) {
20+
- tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout, __vrfname);
21+
+ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname);
22+
if (tac_fd < 0) {
23+
_pam_log(LOG_WARNING, "%s: error sending %s (fd)",
24+
__FUNCTION__, typemsg);
25+
@@ -276,7 +276,7 @@ int pam_sm_authenticate (pam_handle_t * pamh, int flags,
26+
if (ctrl & PAM_TAC_DEBUG)
27+
syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i );
28+
29+
- tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout, __vrfname);
30+
+ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname);
31+
if (tac_fd < 0) {
32+
_pam_log(LOG_ERR, "%s: connection to srv %d failed", __FUNCTION__, srv_i);
33+
continue;
34+
@@ -579,7 +579,7 @@ int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags,
35+
if(tac_protocol[0] != '\0')
36+
tac_add_attrib(&attr, "protocol", tac_protocol);
37+
38+
- tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL, tac_timeout, __vrfname);
39+
+ tac_fd = tac_connect_single(active_server.addr, active_server.key, tac_source_addr, tac_timeout, __vrfname);
40+
if(tac_fd < 0) {
41+
_pam_log (LOG_ERR, "TACACS+ server unavailable");
42+
if(arep.msg != NULL)
43+
@@ -762,7 +762,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
44+
if (ctrl & PAM_TAC_DEBUG)
45+
syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i );
46+
47+
- tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout, __vrfname);
48+
+ tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, tac_source_addr, tac_timeout, __vrfname);
49+
if (tac_fd < 0) {
50+
_pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i);
51+
continue;
52+
diff --git a/support.c b/support.c
53+
index 7c00618..3e55e2f 100644
54+
--- a/support.c
55+
+++ b/support.c
56+
@@ -37,6 +37,8 @@ char tac_service[64];
57+
char tac_protocol[64];
58+
char tac_prompt[64];
59+
char *__vrfname=NULL;
60+
+char tac_source_ip[64];
61+
+struct addrinfo *tac_source_addr = NULL;
62+
63+
void _pam_log(int err, const char *format,...) {
64+
char msg[256];
65+
@@ -183,6 +185,12 @@ int _pam_parse (int argc, const char **argv) {
66+
tac_protocol[0] = 0;
67+
tac_prompt[0] = 0;
68+
tac_login[0] = 0;
69+
+ tac_source_ip[0] = 0;
70+
+
71+
+ if (tac_source_addr != NULL) {
72+
+ freeaddrinfo(tac_source_addr);
73+
+ tac_source_addr = NULL;
74+
+ }
75+
76+
for (ctrl = 0; argc-- > 0; ++argv) {
77+
if (!strcmp (*argv, "debug")) { /* all */
78+
@@ -274,6 +282,10 @@ int _pam_parse (int argc, const char **argv) {
79+
}
80+
} else if(!strncmp(*argv, "vrf=", 4)) {
81+
__vrfname = strdup(*argv + 4);
82+
+ } else if (!strncmp (*argv, "source_ip=", strlen("source_ip="))) {
83+
+ /* source ip for the packets */
84+
+ strncpy (tac_source_ip, *argv + strlen("source_ip="), sizeof(tac_source_ip));
85+
+ set_source_ip (tac_source_ip, &tac_source_addr);
86+
} else {
87+
_pam_log (LOG_WARNING, "unrecognized option: %s", *argv);
88+
}
89+
@@ -292,8 +304,27 @@ int _pam_parse (int argc, const char **argv) {
90+
_pam_log(LOG_DEBUG, "tac_protocol='%s'", tac_protocol);
91+
_pam_log(LOG_DEBUG, "tac_prompt='%s'", tac_prompt);
92+
_pam_log(LOG_DEBUG, "tac_login='%s'", tac_login);
93+
+ _pam_log(LOG_DEBUG, "tac_source_ip='%s'", tac_source_ip);
94+
}
95+
96+
return ctrl;
97+
} /* _pam_parse */
98+
99+
+/* set source ip address for the outgoing tacacs packets */
100+
+void set_source_ip(const char *tac_source_ip,
101+
+ struct addrinfo **source_address) {
102+
+
103+
+ struct addrinfo hints;
104+
+ int rv;
105+
+
106+
+ /* set the source ip address for the tacacs packets */
107+
+ memset(&hints, 0, sizeof(hints));
108+
+ hints.ai_family = AF_UNSPEC;
109+
+ hints.ai_socktype = SOCK_STREAM;
110+
+ if ((rv = getaddrinfo(tac_source_ip, NULL, &hints,
111+
+ source_address)) != 0) {
112+
+ _pam_log(LOG_ERR, "error setting the source ip information");
113+
+ } else {
114+
+ _pam_log(LOG_DEBUG, "source ip is set");
115+
+ }
116+
+}
117+
diff --git a/support.h b/support.h
118+
index 9cbd040..09b8a85 100644
119+
--- a/support.h
120+
+++ b/support.h
121+
@@ -37,6 +37,7 @@ extern int tac_srv_no;
122+
extern char tac_service[64];
123+
extern char tac_protocol[64];
124+
extern char tac_prompt[64];
125+
+extern struct addrinfo *tac_source_addr;
126+
127+
int _pam_parse (int, const char **);
128+
unsigned long _resolve_name (char *);
129+
--
130+
2.7.4
131+

src/tacacs/pam/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
1919
git apply ../0003-Obfuscate-key-before-printing-to-syslog.patch
2020
git apply ../0004-management-vrf-support.patch
2121
git apply ../0005-pam-Modify-parsing-of-IP-address-and-port-number-to-.patch
22+
git apply ../0006-Add-support-for-source-ip-address.patch
2223

2324
dpkg-buildpackage -rfakeroot -b -us -uc
2425
popd

0 commit comments

Comments
 (0)