static void test_valid_user_group_name(void) { log_info("/* %s */", __func__); assert_se(!valid_user_group_name(NULL)); assert_se(!valid_user_group_name("")); assert_se(!valid_user_group_name("1")); assert_se(!valid_user_group_name("65535")); assert_se(!valid_user_group_name("-1")); assert_se(!valid_user_group_name("-kkk")); assert_se(!valid_user_group_name("rööt")); assert_se(!valid_user_group_name(".")); assert_se(!valid_user_group_name("eff.eff")); assert_se(!valid_user_group_name("foo\nbar")); assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); assert_se(!valid_user_group_name_or_id("aaa:bbb")); assert_se(valid_user_group_name("root")); assert_se(valid_user_group_name("lennart")); assert_se(valid_user_group_name("LENNART")); assert_se(valid_user_group_name("_kkk")); assert_se(valid_user_group_name("kkk-")); assert_se(valid_user_group_name("kk-k")); assert_se(valid_user_group_name("some5")); assert_se(!valid_user_group_name("5some")); assert_se(valid_user_group_name("INNER5NUMBER")); }
static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) { _cleanup_close_pair_ int storage_socket[2] = { -1, -1 }; DynamicUser *d; int r; assert(m); assert(name); /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the * allocated UID number, plus an fd referencing the lock file for the UID * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair * may exist in three different states: * * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized. * * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a * statically assigned UID by the same name, which we are reusing. * * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic * UID and locked it in the file system, using the lock fd. * * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here: * the UID lock on disk is protected via a BSD file lock (i.e. an fd-bound lock), so that the lock is kept in * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous, * nobody else could get any access to it except via our own fd) and we want to synchronize access between all * processes that have access to it. */ d = hashmap_get(m->dynamic_users, name); if (d) { if (ret) { /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */ d->n_ref++; *ret = d; } return 0; } if (!valid_user_group_name_or_id(name)) return -EINVAL; if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0) return -errno; r = dynamic_user_add(m, name, storage_socket, &d); if (r < 0) return r; storage_socket[0] = storage_socket[1] = -1; if (ret) { d->n_ref++; *ret = d; } return 1; }