Line data Source code
1 : /* Copyright (c) 2003-2004, Roger Dingledine 2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 : /* See LICENSE for licensing information */ 5 : 6 : /** 7 : * \file userdb.c 8 : * 9 : * \brief Access the POSIX user database. 10 : **/ 11 : 12 : #include "lib/fs/userdb.h" 13 : 14 : #ifndef _WIN32 15 : #include "lib/malloc/malloc.h" 16 : #include "lib/log/log.h" 17 : #include "lib/log/util_bug.h" 18 : 19 : #include <pwd.h> 20 : #include <stddef.h> 21 : #include <string.h> 22 : 23 : /** Cached struct from the last getpwname() call we did successfully. */ 24 : static struct passwd *passwd_cached = NULL; 25 : 26 : /** Helper: copy a struct passwd object. 27 : * 28 : * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use 29 : * any others, and I don't want to run into incompatibilities. 30 : */ 31 : static struct passwd * 32 3 : tor_passwd_dup(const struct passwd *pw) 33 : { 34 3 : struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd)); 35 3 : if (pw->pw_name) 36 3 : new_pw->pw_name = tor_strdup(pw->pw_name); 37 3 : if (pw->pw_dir) 38 3 : new_pw->pw_dir = tor_strdup(pw->pw_dir); 39 3 : new_pw->pw_uid = pw->pw_uid; 40 3 : new_pw->pw_gid = pw->pw_gid; 41 : 42 3 : return new_pw; 43 : } 44 : 45 : #define tor_passwd_free(pw) \ 46 : FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw)) 47 : 48 : /** Helper: free one of our cached 'struct passwd' values. */ 49 : static void 50 238 : tor_passwd_free_(struct passwd *pw) 51 : { 52 238 : if (!pw) 53 : return; 54 : 55 2 : tor_free(pw->pw_name); 56 2 : tor_free(pw->pw_dir); 57 2 : tor_free(pw); 58 : } 59 : 60 : /** Wrapper around getpwnam() that caches result. Used so that we don't need 61 : * to give the sandbox access to /etc/passwd. 62 : * 63 : * The following fields alone will definitely be copied in the output: pw_uid, 64 : * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. 65 : * 66 : * When called with a NULL argument, this function clears storage associated 67 : * with static variables it uses. 68 : **/ 69 : const struct passwd * 70 240 : tor_getpwnam(const char *username) 71 : { 72 240 : struct passwd *pw; 73 : 74 240 : if (username == NULL) { 75 235 : tor_passwd_free(passwd_cached); 76 235 : passwd_cached = NULL; 77 235 : return NULL; 78 : } 79 : 80 5 : if ((pw = getpwnam(username))) { 81 3 : tor_passwd_free(passwd_cached); 82 3 : passwd_cached = tor_passwd_dup(pw); 83 3 : log_info(LD_GENERAL, "Caching new entry %s for %s", 84 : passwd_cached->pw_name, username); 85 3 : return pw; 86 : } 87 : 88 : /* Lookup failed */ 89 2 : if (! passwd_cached || ! passwd_cached->pw_name) 90 : return NULL; 91 : 92 2 : if (! strcmp(username, passwd_cached->pw_name)) 93 : return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky 94 : 95 : return NULL; 96 : } 97 : 98 : /** Wrapper around getpwnam() that can use cached result from 99 : * tor_getpwnam(). Used so that we don't need to give the sandbox access to 100 : * /etc/passwd. 101 : * 102 : * The following fields alone will definitely be copied in the output: pw_uid, 103 : * pw_gid, pw_name, pw_dir. Other fields are not present in cached values. 104 : */ 105 : const struct passwd * 106 3 : tor_getpwuid(uid_t uid) 107 : { 108 3 : struct passwd *pw; 109 : 110 3 : if ((pw = getpwuid(uid))) { 111 : return pw; 112 : } 113 : 114 : /* Lookup failed */ 115 1 : if (! passwd_cached) 116 : return NULL; 117 : 118 1 : if (uid == passwd_cached->pw_uid) 119 : return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky 120 : 121 : return NULL; 122 : } 123 : 124 : /** Allocate and return a string containing the home directory for the 125 : * user <b>username</b>. Only works on posix-like systems. */ 126 : char * 127 2 : get_user_homedir(const char *username) 128 : { 129 2 : const struct passwd *pw; 130 2 : tor_assert(username); 131 : 132 2 : if (!(pw = tor_getpwnam(username))) { 133 1 : log_err(LD_CONFIG,"User \"%s\" not found.", username); 134 1 : return NULL; 135 : } 136 1 : return tor_strdup(pw->pw_dir); 137 : } 138 : #endif /* !defined(_WIN32) */