diff options
author | Dave Olson <olson@cumulusnetworks.com> | 2018-04-12 23:57:55 -0700 |
---|---|---|
committer | Dave Olson <olson@cumulusnetworks.com> | 2018-04-13 15:04:03 -0700 |
commit | acc77c4757775bb7689ba769465951a65523db75 (patch) | |
tree | ac797a2985f5c472f83f42b13acb3499553f5a15 /src/radius_shell.c | |
parent | a0d0d2fb1b321d65425951fc70f5c42c2dcfda41 (diff) | |
download | libpam-radius-auth-acc77c4757775bb7689ba769465951a65523db75.tar.gz libpam-radius-auth-acc77c4757775bb7689ba769465951a65523db75.zip |
Add a new package radius-shell with a setcap radius_shell front end
Ticket: CM-19457
Reviewed By: nobody
Testing Done: multiple logins, separately and simultaneously
Because we can't determine privilege level separately and up front with
the RADIUS protocol, unlike TACACS+, we wind up with all logins as the
same unprivileged radius uid. But we can set the auid (accounting or
auditing uid) correctly, and a separate setcap radius_shell can be set as
the login shell, and can fixup the uid before running /bin/bash.
To set the auid correctly, we need to know the privileged radius user
account. Added mapped_priv_user to the configuration file to handle
that. mapped_priv_user has to match the account used by libnss-mapuser.
That's a bit ugly, but a common config file would be uglier.
The radius shell is in a new package, since it has binaries. The new
package is radius-shell. In it's post actions, it changes the radius
users shell to radius_shell if they are present, and back to /bin/bash
on package removal. It uses capabilities, tries to be very restrictive
in what it changes, and depends on being installed setcap cap_setuid
Make the existing libpam-radius-auth package depend on radius-shell, so
it will pull in the new package on upgrades.
Also fixed another issue with reparsing changed config file, have to
handle case where there were servers defined, but aren't any longer.
Diffstat (limited to 'src/radius_shell.c')
-rw-r--r-- | src/radius_shell.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/radius_shell.c b/src/radius_shell.c new file mode 100644 index 0000000..a94c7f3 --- /dev/null +++ b/src/radius_shell.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 Cumulus Networks, Inc. + * All rights reserved. + * Author: Dave Olson <olson@cumulusnetworks.com> + * + * 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 program exists to set the uid of privileged radius login users. + * Due to the limitations of the RADIUS protocol, we can't determine + * whether a user is privileged or not until they have authenticated, + * and by then, some of the login mechanisms (openssh, e.g.) have already + * determined the uid. + * + * This program looks at the accounting uid, and if set, and not the same + * as the uid, and the auid is >= 1000, will try to reset the uid to the auid + * as well as the fsuid. + * + * For this to work, the program must be installed as setcap cap_setuid. + * As a minor additional safeguard, the program should be installed as + * a member of the radius_users group, and permissions 750. + * + * Errors are written to stderr so the user logging in will see them, + * rather than using syslog. + */ + +#define _GNU_SOURCE +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <libaudit.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <sys/fsuid.h> +#include <sys/capability.h> + +int main(int cnt, char **args) +{ + uid_t uid, auid; + cap_value_t capability[] = { CAP_SETUID}; + cap_t capabilities; + char *shell = NULL, *check = NULL, execshell[64]; + + uid = getuid(); + auid = audit_getloginuid(); + + if (uid < 1000 || auid < 1000 || auid == (uid_t)-1 || uid == auid) { + /* We try to be careful in what we will change */ + goto execit; + } + + if (setfsuid(auid) == -1) + fprintf(stderr, "Failed to set fsuid to %u: %s\n", + auid, strerror(errno)); + if (setresuid(auid, auid, auid)) + fprintf(stderr, "Failed to set uid to %u: %s\n", + auid, strerror(errno)); + if (getuid() != auid) + fprintf(stderr, "Failed to set uid to %u it's still %u\n", + auid, getuid()); + +execit: + /* be paranoid, and clear our expected CAP_SETUID capability, + * even though it should be cleared on exec. + */ + capabilities = cap_get_proc(); + if (capabilities) { + if (!cap_set_flag(capabilities, CAP_EFFECTIVE, 1, + capability, CAP_CLEAR) && + !cap_set_flag(capabilities, CAP_PERMITTED, 1, + capability, CAP_CLEAR)) { + if (cap_set_proc(capabilities)) + fprintf(stderr, "Failed to clear cap_setuid: %s\n", + strerror(errno)); + } + } + +#ifdef LATER + /* + * Eventually handle this program being linked or symlinked + * and that the shell is one of the shells in /etc/shells + */ + shell = strrchr(args[0], '/'); + if (!shell) + shell = args[0]; + + if (*shell == '-') { + check = shell + 1; + } + else + check = shell; + + /* should really check this against /etc/shell */ + snprintf(execshell, sizeof execshell, "/bin/%s", check); +#else + check = "bash"; + shell = "-bash"; + snprintf(execshell, sizeof execshell, "/bin/%s", check); +#endif + + execl(execshell, shell, NULL); + fprintf(stderr, "Exec of shell %s failed: %s\n", execshell, + strerror(errno)); + exit(1); +} |