aboutsummaryrefslogtreecommitdiff
path: root/src/lib/fs/userdb.c
blob: 95205c670ee639213392e7d76c04922ef16be458 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* Copyright (c) 2003-2004, Roger Dingledine
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
 * Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file userdb.c
 *
 * \brief Access the POSIX user database.
 **/

#include "lib/fs/userdb.h"

#ifndef _WIN32
#include "lib/malloc/malloc.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"

#include <pwd.h>
#include <stddef.h>
#include <string.h>

/** Cached struct from the last getpwname() call we did successfully. */
static struct passwd *passwd_cached = NULL;

/** Helper: copy a struct passwd object.
 *
 * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir.  Tor doesn't use
 * any others, and I don't want to run into incompatibilities.
 */
static struct passwd *
tor_passwd_dup(const struct passwd *pw)
{
  struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd));
  if (pw->pw_name)
    new_pw->pw_name = tor_strdup(pw->pw_name);
  if (pw->pw_dir)
    new_pw->pw_dir = tor_strdup(pw->pw_dir);
  new_pw->pw_uid = pw->pw_uid;
  new_pw->pw_gid = pw->pw_gid;

  return new_pw;
}

#define tor_passwd_free(pw) \
  FREE_AND_NULL(struct passwd, tor_passwd_free_, (pw))

/** Helper: free one of our cached 'struct passwd' values. */
static void
tor_passwd_free_(struct passwd *pw)
{
  if (!pw)
    return;

  tor_free(pw->pw_name);
  tor_free(pw->pw_dir);
  tor_free(pw);
}

/** Wrapper around getpwnam() that caches result. Used so that we don't need
 * to give the sandbox access to /etc/passwd.
 *
 * The following fields alone will definitely be copied in the output: pw_uid,
 * pw_gid, pw_name, pw_dir.  Other fields are not present in cached values.
 *
 * When called with a NULL argument, this function clears storage associated
 * with static variables it uses.
 **/
const struct passwd *
tor_getpwnam(const char *username)
{
  struct passwd *pw;

  if (username == NULL) {
    tor_passwd_free(passwd_cached);
    passwd_cached = NULL;
    return NULL;
  }

  if ((pw = getpwnam(username))) {
    tor_passwd_free(passwd_cached);
    passwd_cached = tor_passwd_dup(pw);
    log_info(LD_GENERAL, "Caching new entry %s for %s",
             passwd_cached->pw_name, username);
    return pw;
  }

  /* Lookup failed */
  if (! passwd_cached || ! passwd_cached->pw_name)
    return NULL;

  if (! strcmp(username, passwd_cached->pw_name))
    return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky

  return NULL;
}

/** Wrapper around getpwnam() that can use cached result from
 * tor_getpwnam(). Used so that we don't need to give the sandbox access to
 * /etc/passwd.
 *
 * The following fields alone will definitely be copied in the output: pw_uid,
 * pw_gid, pw_name, pw_dir.  Other fields are not present in cached values.
 */
const struct passwd *
tor_getpwuid(uid_t uid)
{
  struct passwd *pw;

  if ((pw = getpwuid(uid))) {
    return pw;
  }

  /* Lookup failed */
  if (! passwd_cached)
    return NULL;

  if (uid == passwd_cached->pw_uid)
    return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky

  return NULL;
}

/** Allocate and return a string containing the home directory for the
 * user <b>username</b>. Only works on posix-like systems. */
char *
get_user_homedir(const char *username)
{
  const struct passwd *pw;
  tor_assert(username);

  if (!(pw = tor_getpwnam(username))) {
    log_err(LD_CONFIG,"User \"%s\" not found.", username);
    return NULL;
  }
  return tor_strdup(pw->pw_dir);
}
#endif /* !defined(_WIN32) */