From 95eb83c42431e9cae43e25bb501ddffb9cf1fe33 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Thu, 12 Oct 2017 12:19:46 -0500 Subject: [PATCH] roken: Add roken_get_username() and friends We add roken_get_{shell, username, appdatadir, homedir}() functions. These use a combination of secure_getenv(), getpwuid_r(), getlogin_r(), or various WIN32 functions to get this information. Use roken_get_appdatadir() instead of roken_get_homedir() when looking for dotfiles. --- lib/krb5/expand_path.c | 16 +- lib/roken/Makefile.am | 4 + lib/roken/NTMakefile | 7 +- lib/roken/getuserinfo.c | 334 +++++++++++++++++++++++++++++++++++ lib/roken/roken.h.in | 9 + lib/roken/test-getuserinfo.c | 107 +++++++++++ lib/roken/version-script.map | 4 + 7 files changed, 472 insertions(+), 9 deletions(-) create mode 100644 lib/roken/getuserinfo.c create mode 100644 lib/roken/test-getuserinfo.c diff --git a/lib/krb5/expand_path.c b/lib/krb5/expand_path.c index c80329347..98e304fa4 100644 --- a/lib/krb5/expand_path.c +++ b/lib/krb5/expand_path.c @@ -311,17 +311,17 @@ _expand_euid(krb5_context context, PTYPE param, const char *postfix, char **str) static krb5_error_code _expand_username(krb5_context context, PTYPE param, const char *postfix, char **str) { - uid_t uid = geteuid(); - struct passwd *pwd, pw; - char pwbuf[2048]; + char user[128]; + const char *username = roken_get_username(user, sizeof(user)); - if (rk_getpwuid_r(uid, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) { - krb5_set_error_message(context, ENOENT, - "Could not find username for UID '%ld'", (long)uid); - return ENOENT; + if (username == NULL) { + krb5_set_error_message(context, ENOTTY, + N_("unable to figure out current principal", + "")); + return ENOTTY; /* XXX */ } - *str = strdup(pwd->pw_name); + *str = strdup(username); if (*str == NULL) return krb5_enomem(context); diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am index 07fcd8008..513516c9e 100644 --- a/lib/roken/Makefile.am +++ b/lib/roken/Makefile.am @@ -31,6 +31,7 @@ check_PROGRAMS = \ getxxyyy-test \ hex-test \ test-auxval \ + test-getuserinfo \ test-readenv \ resolve-test \ parse_bytes-test \ @@ -54,6 +55,8 @@ parse_reply_test_CFLAGS = -DTEST_RESOLVE test_readenv_SOURCES = test-readenv.c test-mem.c test_auxval_SOURCES = test-auxval.c +test_getuserinfo_SOURCES = test-getuserinfo.c +test_getuserinfo_LDADD = libtest.la $(LDADD) test_detach_SOURCES = test-detach.c @@ -99,6 +102,7 @@ libroken_la_SOURCES = \ getauxval.h \ getnameinfo_verified.c \ getprogname.c \ + getuserinfo.c \ getxxyyy.c \ h_errno.c \ hex.c \ diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile index 4daf3ca60..023bd130e 100644 --- a/lib/roken/NTMakefile +++ b/lib/roken/NTMakefile @@ -67,6 +67,7 @@ libroken_la_OBJS = \ $(OBJ)\getopt.obj \ $(OBJ)\getprogname.obj \ $(OBJ)\gettimeofday.obj \ + $(OBJ)\getuserinfo.obj \ $(OBJ)\hex.obj \ $(OBJ)\hostent_find_fqdn.obj \ $(OBJ)\inet_aton.obj \ @@ -184,7 +185,7 @@ all:: $(INCFILES) $(LIBROKEN) clean:: -$(RM) $(LIBROKEN) -TMP_PROGS = $(OBJ)\snprintf-test.exe $(OBJ)\resolve-test.exe +TMP_PROGS = $(OBJ)\snprintf-test.exe $(OBJ)\resolve-test.exe $(OBJ)\test-getuserinfo.exe # Tests @@ -194,6 +195,7 @@ TEST_PROGS = \ $(OBJ)\getifaddrs-test.exe \ $(OBJ)\hex-test.exe \ $(OBJ)\test-detach.exe \ + $(OBJ)\test-getuserinfo.exe \ $(OBJ)\test-readenv.exe \ $(OBJ)\parse_bytes-test.exe \ $(OBJ)\parse_reply-test.exe \ @@ -260,6 +262,9 @@ $(OBJ)\parse_bytes-test.exe: $(OBJ)\parse_bytes-test.obj $(LIBROKEN) $(OBJ)\test-detach.exe: $(OBJ)\test-detach.obj $(OBJ)\detach.obj $(LIBROKEN) $(EXECONLINK) +$(OBJ)\test-getuserinfo.exe: $(OBJ)\test-getuserinfo.obj $(OBJ)\getuserinfo.obj $(LIBROKEN) + $(EXECONLINK) Secur32.lib Shell32.lib + $(OBJ)\dirent-test.exe: $(OBJ)\dirent-test.obj $(LIBROKEN) $(EXECONLINK) diff --git a/lib/roken/getuserinfo.c b/lib/roken/getuserinfo.c new file mode 100644 index 000000000..29b7d2144 --- /dev/null +++ b/lib/roken/getuserinfo.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2017 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "roken.h" + +#ifdef WIN32 +#include // need to include definitions of constants +#define SECURITY_WIN32 +#include +#else +#include +#include +#include +#endif + +/** + * Returns the user's SHELL. + */ +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_shell(char *shell, size_t shellsz) +{ + char *p; + +#ifndef WIN32 + char user[128]; + const char *username = roken_get_username(user, sizeof(user)); + size_t buflen = 2048; + + if (sysconf(_SC_GETPW_R_SIZE_MAX) > 0) + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (issuid()) + return "/bin/sh"; + + p = secure_getenv("SHELL"); + if (p != NULL && p[0] != '\0') { + if (strlcpy(shell, p, shellsz) < shellsz) + return shell; + errno = ERANGE; + return NULL; + } + +#ifdef HAVE_GETPWNAM_R + { + struct passwd pwd; + struct passwd *pwdp; + char buf[buflen]; + + if (getpwnam_r(username, &pwd, buf, buflen, &pwdp) == 0 && + pwdp != NULL && pwdp->pw_shell != NULL) { + if (strlcpy(shell, pwdp->pw_shell, shellsz) < shellsz) + return shell; + errno = ERANGE; + return NULL; + } + } +#endif + return "/bin/sh"; +#else + /* Windows */ + p = getenv("SHELL"); + if (p != NULL && p[0] != '\0') { + if (strlcpy(shell, p, shellsz) < shellsz) + return shell; + errno = ERANGE; + return NULL; + } +#endif + errno = 0; + return NULL; +} + +/** + * Returns the home directory. + */ +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_homedir(char *home, size_t homesz) +{ + char *p; + +#ifdef WIN32 + if (homesz < MAX_PATH) { + errno = ERANGE; + return NULL; + } + + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, + SHGFP_TYPE_CURRENT, home))) + return home; + + if ((p = getenv("HOMEDRIVE")) != NULL && p[0] != '\0') { + if (strlcpy(home, p, homesz) >= homesz) { + errno = ERANGE; + return NULL; + } + if ((p = getenv("HOMEPATH")) != NULL) { + if (strlcat(home, p, homesz) < homesz) + return home; + errno = ERANGE; + return NULL; + } + return home; + } + /* Fallthru to return NULL */ +#else + char user[128]; + const char *username = roken_get_username(user, sizeof(user)); + size_t buflen = 2048; + + if (sysconf(_SC_GETPW_R_SIZE_MAX) > 0) + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (issuid()) { + errno = 0; + return NULL; + } + + p = secure_getenv("HOME"); + if (p != NULL && p[0] != '\0') { + if (strlcpy(home, p, homesz) < homesz) + return home; + errno = ERANGE; + return NULL; + } + +#ifdef HAVE_GETPWNAM_R + { + struct passwd pwd; + struct passwd *pwdp; + char buf[buflen]; + + if (getpwnam_r(username, &pwd, buf, buflen, &pwdp) == 0 && + pwdp != NULL && pwdp->pw_dir != NULL) { + if (strlcpy(home, pwdp->pw_dir, homesz) < homesz) + return home; + errno = ERANGE; + return NULL; + } + } +#endif +#endif + errno = 0; + return NULL; +} + +/** + * Returns the home directory on Unix, or the AppData directory on + * Windows. + */ +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_appdatadir(char *appdata, size_t appdatasz) +{ +#ifdef WIN32 + char *p; +#endif + +#ifndef WIN32 + return roken_get_homedir(appdata, appdatasz); +#else + if (appdatasz < MAX_PATH) { + errno = ERANGE; + return NULL; + } + + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, + SHGFP_TYPE_CURRENT, appdata))) + return appdata; + + if ((p = getenv("APPDATA")) != NULL && p[0] != '\0') { + if (strlcpy(appdata, p, appdatasz) < appdatasz) + return appdata; + errno = ERANGE; + return NULL; + } + + errno = 0; + return NULL; +#endif +} + +/** + * Return a bare username. This is used for, e.g., constructing default + * principal names. + */ +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_username(char *user, size_t usersz) +{ + char *p; + +#ifdef WIN32 + ULONG sz = usersz; + + if (GetUserNameEx(NameSamCompatible, user, &sz)) { + /* + * There's no EXTENDED_NAME_FORMAT for "bare username". We we + * have to parse one. + */ + p = strchr(user, '\\'); + if (p != NULL) { + p++; + memmove(user, p, strlen(p)); + } + return user; + } else { + DWORD err = GetLastError(); + if (err == ERROR_MORE_DATA) { + errno = ERANGE; + return NULL; + } + /* %USERNAME% is generally bare */ + p = getenv("USERNAME"); + if (p != NULL && p[0] != '\0') { + if (strchr(p, '\\') != NULL) + p = strchr(p, '\\') + 1; + if (strlcpy(user, p, usersz) < usersz) + return user; + errno = ERANGE; + return NULL; + } + } +#else + size_t buflen = 2048; + + if (issuid()) + return NULL; + + if (getuid() == 0) { + /* + * NOTE: When the running process was not the result of executing a + * set-uid or set-gid executable, and it is running as root, then the + * username will preferentially be taken from the wtmp if at all + * possible. This is depended on by _krb5_get_default_principal_local() + * in order to produce /root@DEFAULT_REALM as the default + * principal. + */ +#ifdef HAVE_GETLOGIN_R + if ((errno = getlogin_r(user, usersz)) == 0) + return user; + if (errno != ENOENT) + return NULL; +#else +#ifdef HAVE_GETLOGIN + if ((p = getlogin()) != NULL && p[0] != '\0') { + if strlcpy(user, p, usersz) < usersz) + return user; + errno = ERANGE; + return NULL; + } + if (errno != ENOENT) + return NULL; +#endif +#endif + } + + p = secure_getenv("USER"); + if (p == NULL || p[0] == '\0') + p = secure_getenv("LOGNAME"); + if (p != NULL && p[0] != '\0') { + if (strlcpy(user, p, usersz) < usersz) + return user; + errno = ERANGE; + return NULL; + } + +#ifdef HAVE_GETPWUID_R + if (sysconf(_SC_GETPW_R_SIZE_MAX) > 0) + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + { + struct passwd pwd; + struct passwd *pwdp; + char buf[buflen]; + + if (getpwuid_r(getuid(), &pwd, buf, buflen, &pwdp) == 0 && + pwdp != NULL && pwdp->pw_name != NULL) { + if (strlcpy(user, pwdp->pw_name, usersz) < usersz) + return user; + errno = ERANGE; + return NULL; + } + } +#endif +#ifdef HAVE_GETLOGIN_R + if ((errno = getlogin_r(user, usersz)) == 0) + return user; + if (errno != ENOENT) + return NULL; +#else +#ifdef HAVE_GETLOGIN + if ((p = getlogin()) != NULL && p[0] != '\0') { + if (strlcpy(user, p, usersz) < usersz) + return user; + errno = ERANGE; + return NULL; + } + if (errno != ENOENT) + return NULL; +#endif +#endif +#endif + errno = 0; + return NULL; +} diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index ac8ee5ab6..d4bb416c8 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -1010,6 +1010,15 @@ roken_getaddrinfo_hostspec(const char *, int, struct addrinfo **); ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_getaddrinfo_hostspec2(const char *, int, int, struct addrinfo **); +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_homedir(char *, size_t); +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_appdatadir(char *, size_t); +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_username(char *, size_t); +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +roken_get_shell(char *, size_t); + #ifndef HAVE_STRFTIME #define strftime rk_strftime ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL diff --git a/lib/roken/test-getuserinfo.c b/lib/roken/test-getuserinfo.c new file mode 100644 index 000000000..e6d5b3140 --- /dev/null +++ b/lib/roken/test-getuserinfo.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef WIN32 +#include +#endif +#include "roken.h" + +int +main(void) +{ + char buf[MAX_PATH * 2]; + char buf2[MAX_PATH * 2]; +#ifndef WIN32 + int ret = 0; + if (!issuid() && getuid() != 0) { + if (getenv("USER") != NULL && strlen(getenv("USER")) != 0 && + strcmp(getenv("USER"), + roken_get_username(buf, sizeof(buf))) != 0) { + warnx("roken_get_username() != getenv(\"USER\")"); + ret++; + } + if (getenv("HOME") != NULL && strlen(getenv("HOME")) != 0 && + strcmp(getenv("HOME"), roken_get_homedir(buf, sizeof(buf))) != 0) { + warnx("roken_get_homedir() != getenv(\"HOME\")"); + ret++; + } + if (getenv("HOME") != NULL && strlen(getenv("HOME")) != 0 && + strcmp(roken_get_appdatadir(buf, sizeof(buf)), + roken_get_homedir(buf2, sizeof(buf2))) != 0) { + warnx("roken_get_homedir() != roken_get_appdatadir()"); + ret++; + } + if (getenv("SHELL") != NULL && strlen(getenv("SHELL")) != 0 && + strcmp(getenv("SHELL"), roken_get_shell(buf, sizeof(buf))) != 0) { + warnx("roken_get_shell() != getenv(\"SHELL\")"); + ret++; + } + } +#endif + printf("Username:\t%s\n", roken_get_username(buf, sizeof(buf))); + printf("Home:\t\t%s\n", roken_get_homedir(buf, sizeof(buf))); + printf("Appdatadir:\t%s\n", roken_get_appdatadir(buf, sizeof(buf))); + printf("Shell:\t\t%s\n", roken_get_shell(buf, sizeof(buf))); + +#ifndef WIN32 + if (!issuid() && getuid() != 0) { + putenv("USER=h5lfoouser"); + putenv("HOME=/no/such/dir/h5lfoouser"); + putenv("SHELL=/no/such/shell"); + if (strcmp("h5lfoouser", roken_get_username(buf, sizeof(buf))) != 0) { + warnx("roken_get_username() (%s) did not honor $USER", + roken_get_username(buf, sizeof(buf))); + ret++; + } + if (strcmp("/no/such/dir/h5lfoouser", + roken_get_homedir(buf, sizeof(buf))) != 0) { + warnx("roken_get_homedir() (%s) did not honor $HOME", + roken_get_homedir(buf, sizeof(buf))); + ret++; + } + if (strcmp(roken_get_appdatadir(buf, sizeof(buf)), + roken_get_homedir(buf2, sizeof(buf2))) != 0) { + warnx("roken_get_homedir() != roken_get_appdatadir() (%s)", + roken_get_appdatadir(buf, sizeof(buf))); + ret++; + } + if (strcmp("/no/such/shell", roken_get_shell(buf, sizeof(buf))) != 0) { + warnx("roken_get_shell() (%s) did not honor $SHELL", + roken_get_shell(buf, sizeof(buf))); + ret++; + } + } + return ret; +#endif + return 0; +} diff --git a/lib/roken/version-script.map b/lib/roken/version-script.map index 51ac91da1..3b241463b 100644 --- a/lib/roken/version-script.map +++ b/lib/roken/version-script.map @@ -179,6 +179,10 @@ HEIMDAL_ROKEN_1.0 { roken_gethostby_setup; roken_gethostbyaddr; roken_gethostbyname; + roken_get_appdatadir; + roken_get_homedir; + roken_get_shell; + roken_get_username; roken_mconcat; roken_vconcat; roken_vmconcat;