user-util.c revision b5efdb8af40ea759a1ea584c1bc44ecc81dd00ce
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/***
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen This file is part of systemd.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Copyright 2010 Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is free software; you can redistribute it and/or modify it
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen under the terms of the GNU Lesser General Public License as published by
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen (at your option) any later version.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is distributed in the hope that it will be useful, but
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Lesser General Public License for more details.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen You should have received a copy of the GNU Lesser General Public License
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen***/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include <pwd.h>
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include <grp.h>
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering
430f0182b72373145c839dbfe99d2382855cb8f8Lennart Poettering#include "alloc-util.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "fd-util.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "macro.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "parse-util.h"
d7b8eec7dc7fe307d3a08b32cf1a9ad4276ce6d5Lennart Poettering#include "path-util.h"
24882e06c135584f16f31ba8a00fecde8b7f6fadLennart Poettering#include "string-util.h"
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include "user-util.h"
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include "util.h"
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringbool uid_is_valid(uid_t uid) {
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen /* Some libc APIs use UID_INVALID as special placeholder */
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return false;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (uid == (uid_t) UINT32_C(0xFFFF))
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return false;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return true;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen}
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringint parse_uid(const char *s, uid_t *ret) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen uint32_t uid = 0;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen int r;
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek assert(s);
cc56fafeebf814ef035e549115cf1850e6473fa5WaLyong Cho
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek assert_cc(sizeof(uid_t) == sizeof(uint32_t));
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt r = safe_atou32(s, &uid);
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek if (r < 0)
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek return r;
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen if (!uid_is_valid(uid))
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen return -ENXIO; /* we return ENXIO instead of EINVAL
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt * here, to make it easy to distuingish
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * invalid numeric uids invalid
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen * strings. */
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (ret)
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen *ret = uid;
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt return 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersenchar* getlogname_malloc(void) {
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen uid_t uid;
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen struct stat st;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
4d506d6bb757af3b99e0876234c465e6898c5ea4Lennart Poettering uid = st.st_uid;
b9e7a9d870ac41d4db954edd52a1f5dd7d153389Lennart Poettering else
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen uid = getuid();
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt return uid_to_name(uid);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersenchar *getusername_malloc(void) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering const char *e;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt e = getenv("USER");
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (e)
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering return strdup(e);
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt return uid_to_name(getuid());
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering}
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poetteringint get_user_creds(
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering const char **username,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering uid_t *uid, gid_t *gid,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering const char **home,
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering const char **shell) {
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen struct passwd *p;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen uid_t u;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen assert(username);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen assert(*username);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen /* We enforce some special rules for uid=0: in order to avoid
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt * NSS lookups for root we hardcode its data. */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (streq(*username, "root") || streq(*username, "0")) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen *username = "root";
96e6e394431dcc1db52847be311e2c8e61d7a9d6Lennart Poettering
96e6e394431dcc1db52847be311e2c8e61d7a9d6Lennart Poettering if (uid)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering *uid = 0;
af4ec4309e8f82aad87a8d574785c12f8763d5f8Lennart Poettering
b37d45c9ab5f645502695e47d268af1a54216e0eTom Gundersen if (gid)
af4ec4309e8f82aad87a8d574785c12f8763d5f8Lennart Poettering *gid = 0;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (home)
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen *home = "/root";
if (shell)
*shell = "/bin/sh";
return 0;
}
if (parse_uid(*username, &u) >= 0) {
errno = 0;
p = getpwuid(u);
/* If there are multiple users with the same id, make
* sure to leave $USER to the configured value instead
* of the first occurrence in the database. However if
* the uid was configured by a numeric uid, then let's
* pick the real username from /etc/passwd. */
if (p)
*username = p->pw_name;
} else {
errno = 0;
p = getpwnam(*username);
}
if (!p)
return errno > 0 ? -errno : -ESRCH;
if (uid) {
if (!uid_is_valid(p->pw_uid))
return -EBADMSG;
*uid = p->pw_uid;
}
if (gid) {
if (!gid_is_valid(p->pw_gid))
return -EBADMSG;
*gid = p->pw_gid;
}
if (home)
*home = p->pw_dir;
if (shell)
*shell = p->pw_shell;
return 0;
}
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
assert(groupname);
/* We enforce some special rules for gid=0: in order to avoid
* NSS lookups for root we hardcode its data. */
if (streq(*groupname, "root") || streq(*groupname, "0")) {
*groupname = "root";
if (gid)
*gid = 0;
return 0;
}
if (parse_gid(*groupname, &id) >= 0) {
errno = 0;
g = getgrgid(id);
if (g)
*groupname = g->gr_name;
} else {
errno = 0;
g = getgrnam(*groupname);
}
if (!g)
return errno > 0 ? -errno : -ESRCH;
if (gid) {
if (!gid_is_valid(g->gr_gid))
return -EBADMSG;
*gid = g->gr_gid;
}
return 0;
}
char* uid_to_name(uid_t uid) {
char *ret;
int r;
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
if (uid_is_valid(uid)) {
long bufsize;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize <= 0)
bufsize = 4096;
for (;;) {
struct passwd pwbuf, *pw = NULL;
_cleanup_free_ char *buf = NULL;
buf = malloc(bufsize);
if (!buf)
return NULL;
r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
if (r == 0 && pw)
return strdup(pw->pw_name);
if (r != ERANGE)
break;
bufsize *= 2;
}
}
if (asprintf(&ret, UID_FMT, uid) < 0)
return NULL;
return ret;
}
char* gid_to_name(gid_t gid) {
char *ret;
int r;
if (gid == 0)
return strdup("root");
if (gid_is_valid(gid)) {
long bufsize;
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
if (bufsize <= 0)
bufsize = 4096;
for (;;) {
struct group grbuf, *gr = NULL;
_cleanup_free_ char *buf = NULL;
buf = malloc(bufsize);
if (!buf)
return NULL;
r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
if (r == 0 && gr)
return strdup(gr->gr_name);
if (r != ERANGE)
break;
bufsize *= 2;
}
}
if (asprintf(&ret, GID_FMT, gid) < 0)
return NULL;
return ret;
}
int in_gid(gid_t gid) {
gid_t *gids;
int ngroups_max, r, i;
if (getgid() == gid)
return 1;
if (getegid() == gid)
return 1;
if (!gid_is_valid(gid))
return -EINVAL;
ngroups_max = sysconf(_SC_NGROUPS_MAX);
assert(ngroups_max > 0);
gids = alloca(sizeof(gid_t) * ngroups_max);
r = getgroups(ngroups_max, gids);
if (r < 0)
return -errno;
for (i = 0; i < r; i++)
if (gids[i] == gid)
return 1;
return 0;
}
int in_group(const char *name) {
int r;
gid_t gid;
r = get_group_creds(&name, &gid);
if (r < 0)
return r;
return in_gid(gid);
}
int get_home_dir(char **_h) {
struct passwd *p;
const char *e;
char *h;
uid_t u;
assert(_h);
/* Take the user specified one */
e = secure_getenv("HOME");
if (e && path_is_absolute(e)) {
h = strdup(e);
if (!h)
return -ENOMEM;
*_h = h;
return 0;
}
/* Hardcode home directory for root to avoid NSS */
u = getuid();
if (u == 0) {
h = strdup("/root");
if (!h)
return -ENOMEM;
*_h = h;
return 0;
}
/* Check the database... */
errno = 0;
p = getpwuid(u);
if (!p)
return errno > 0 ? -errno : -ESRCH;
if (!path_is_absolute(p->pw_dir))
return -EINVAL;
h = strdup(p->pw_dir);
if (!h)
return -ENOMEM;
*_h = h;
return 0;
}
int get_shell(char **_s) {
struct passwd *p;
const char *e;
char *s;
uid_t u;
assert(_s);
/* Take the user specified one */
e = getenv("SHELL");
if (e) {
s = strdup(e);
if (!s)
return -ENOMEM;
*_s = s;
return 0;
}
/* Hardcode home directory for root to avoid NSS */
u = getuid();
if (u == 0) {
s = strdup("/bin/sh");
if (!s)
return -ENOMEM;
*_s = s;
return 0;
}
/* Check the database... */
errno = 0;
p = getpwuid(u);
if (!p)
return errno > 0 ? -errno : -ESRCH;
if (!path_is_absolute(p->pw_shell))
return -EINVAL;
s = strdup(p->pw_shell);
if (!s)
return -ENOMEM;
*_s = s;
return 0;
}
int reset_uid_gid(void) {
if (setgroups(0, NULL) < 0)
return -errno;
if (setresgid(0, 0, 0) < 0)
return -errno;
if (setresuid(0, 0, 0) < 0)
return -errno;
return 0;
}
int take_etc_passwd_lock(const char *root) {
struct flock flock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0,
};
const char *path;
int fd, r;
/* This is roughly the same as lckpwdf(), but not as awful. We
* don't want to use alarm() and signals, hence we implement
* our own trivial version of this.
*
* Note that shadow-utils also takes per-database locks in
* addition to lckpwdf(). However, we don't given that they
* are redundant as they they invoke lckpwdf() first and keep
* it during everything they do. The per-database locks are
* awfully racy, and thus we just won't do them. */
if (root)
path = prefix_roota(root, "/etc/.pwd.lock");
else
path = "/etc/.pwd.lock";
fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
if (fd < 0)
return -errno;
r = fcntl(fd, F_SETLKW, &flock);
if (r < 0) {
safe_close(fd);
return -errno;
}
return fd;
}