Compare commits

..

No commits in common. "47b04ef6327bac408ed724f4e94967ed36b72877" and "f77d6ec9539fd87eb3ecad3a742ac684cc6dc5d2" have entirely different histories.

48 changed files with 532 additions and 740 deletions

3
TODO
View File

@ -4,9 +4,6 @@ Bugfixes:
manager or system manager can be always set. It would be better to reject manager or system manager can be always set. It would be better to reject
them when parsing config. them when parsing config.
* userdbctl: "Password OK: yes" is shown even when there are no passwords
or the password is locked.
External: External:
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros. * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.

View File

@ -415,7 +415,6 @@ Most socket unit settings are available to transient units.
✓ SocketMode= ✓ SocketMode=
✓ DirectoryMode= ✓ DirectoryMode=
✓ Accept= ✓ Accept=
✓ FlushPending=
✓ Writable= ✓ Writable=
✓ MaxConnections= ✓ MaxConnections=
✓ MaxConnectionsPerSource= ✓ MaxConnectionsPerSource=

View File

@ -3950,8 +3950,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false") @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u NRefused = ...; readonly u NRefused = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false") @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u FlushPending = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s FileDescriptorName = '...'; readonly s FileDescriptorName = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SocketProtocol = ...; readonly i SocketProtocol = ...;
@ -5033,8 +5031,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="NRefused"/> <variablelist class="dbus-property" generated="True" extra-ref="NRefused"/>
<variablelist class="dbus-property" generated="True" extra-ref="FlushPending"/>
<variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorName"/> <variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorName"/>
<variablelist class="dbus-property" generated="True" extra-ref="SocketProtocol"/> <variablelist class="dbus-property" generated="True" extra-ref="SocketProtocol"/>
@ -5512,10 +5508,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
meaning as they have for the corresponding field of service units (see above). In addition to that, meaning as they have for the corresponding field of service units (see above). In addition to that,
the value <literal>service-failed-permanent</literal> indicates that the service of this socket failed the value <literal>service-failed-permanent</literal> indicates that the service of this socket failed
continuously.</para> continuously.</para>
<para><varname>FlushPending</varname> specifies whether to flush the socket
just before entering the listening state. This setting only applies to sockets with
<varname>Accept=</varname> set to <literal>no</literal>.</para>
</refsect2> </refsect2>
</refsect1> </refsect1>

View File

@ -427,18 +427,6 @@
false, in read-only mode. Defaults to false.</para></listitem> false, in read-only mode. Defaults to false.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>FlushPending=</varname></term>
<listitem><para>Takes a boolean argument. May only be used when
<option>Accept=no</option>. If yes, the socket's buffers are cleared after the
triggered service exited. This causes any pending data to be
flushed and any pending incoming connections to be rejected. If no, the
socket's buffers won't be cleared, permitting the service to handle any
pending connections after restart, which is the usually expected behaviour.
Defaults to <option>no</option>.
</para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>MaxConnections=</varname></term> <term><varname>MaxConnections=</varname></term>
<listitem><para>The maximum number of connections to <listitem><para>The maximum number of connections to

View File

@ -59,13 +59,7 @@
user friendly, human readable output is generated; if <literal>table</literal> a minimal, tabular user friendly, human readable output is generated; if <literal>table</literal> a minimal, tabular
output is generated; if <literal>json</literal> a JSON formatted output is generated. Defaults to output is generated; if <literal>json</literal> a JSON formatted output is generated. Defaults to
<literal>friendly</literal> if a user/group is specified on the command line, <literal>friendly</literal> if a user/group is specified on the command line,
<literal>table</literal> otherwise.</para> <literal>table</literal> otherwise.</para></listitem>
<para>Note that most output formats do not show all available information. In particular,
<literal>classic</literal> and <literal>table</literal> show only the most important fields. Various
modes also do not show password hashes. Use <literal>json</literal> to view all fields, including
any authentication fields.</para>
</listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -161,21 +161,28 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
return 0; return 0;
} }
int capability_gain_cap_setpcap(cap_t *ret_before_caps) { int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t caps = NULL; _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
cap_flag_value_t fv; cap_flag_value_t fv;
caps = cap_get_proc(); int r;
if (!caps)
/* If we are run as PID 1 we will lack CAP_SETPCAP by default
* in the effective set (yes, the kernel drops that when
* executing init!), so get it back temporarily so that we can
* call PR_CAPBSET_DROP. */
before_cap = cap_get_proc();
if (!before_cap)
return -errno; return -errno;
if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) if (cap_get_flag(before_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
return -errno; return -errno;
if (fv != CAP_SET) { if (fv != CAP_SET) {
_cleanup_cap_free_ cap_t temp_cap = NULL; _cleanup_cap_free_ cap_t temp_cap = NULL;
static const cap_value_t v = CAP_SETPCAP; static const cap_value_t v = CAP_SETPCAP;
temp_cap = cap_dup(caps); temp_cap = cap_dup(before_cap);
if (!temp_cap) if (!temp_cap)
return -errno; return -errno;
@ -186,27 +193,8 @@ int capability_gain_cap_setpcap(cap_t *ret_before_caps) {
log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m"); log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m");
/* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means /* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means
* we'll fail later, when we actually intend to drop some capabilities or try to set securebits. */ * we'll fail later, when we actually intend to drop some capabilities. */
} }
if (ret_before_caps)
/* Return the capabilities as they have been before setting CAP_SETPCAP */
*ret_before_caps = TAKE_PTR(caps);
return 0;
}
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
int r;
/* If we are run as PID 1 we will lack CAP_SETPCAP by default
* in the effective set (yes, the kernel drops that when
* executing init!), so get it back temporarily so that we can
* call PR_CAPBSET_DROP. */
r = capability_gain_cap_setpcap(&before_cap);
if (r < 0)
return r;
after_cap = cap_dup(before_cap); after_cap = cap_dup(before_cap);
if (!after_cap) if (!after_cap)

View File

@ -14,7 +14,6 @@
unsigned cap_last_cap(void); unsigned cap_last_cap(void);
int have_effective_cap(int value); int have_effective_cap(int value);
int capability_gain_cap_setpcap(cap_t *return_caps);
int capability_bounding_set_drop(uint64_t keep, bool right_now); int capability_bounding_set_drop(uint64_t keep, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t keep); int capability_bounding_set_drop_usermode(uint64_t keep);

View File

@ -730,6 +730,10 @@ bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **ke
return true; return true;
} }
bool set_iterate(const Set *s, Iterator *i, void **value) {
return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
}
#define HASHMAP_FOREACH_IDX(idx, h, i) \ #define HASHMAP_FOREACH_IDX(idx, h, i) \
for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
(idx != IDX_NIL); \ (idx != IDX_NIL); \
@ -844,16 +848,6 @@ int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBU
return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
} }
int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) {
int r;
r = _ordered_hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return ordered_hashmap_put(*h, key, value);
}
static void hashmap_free_no_clear(HashmapBase *h) { static void hashmap_free_no_clear(HashmapBase *h) {
assert(!h->has_indirect); assert(!h->has_indirect);
assert(h->n_direct_entries == 0); assert(h->n_direct_entries == 0);

View File

@ -137,9 +137,6 @@ int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops
#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) #define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
#define ordered_hashmap_ensure_put(s, ops, key, value) _ordered_hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h); IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h);
static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h)); return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));

View File

@ -5,7 +5,6 @@
#include <unistd.h> #include <unistd.h>
#include "btrfs-util.h" #include "btrfs-util.h"
#include "fs-util.h"
#include "label.h" #include "label.h"
#include "macro.h" #include "macro.h"
#include "selinux-util.h" #include "selinux-util.h"
@ -46,27 +45,6 @@ int symlink_label(const char *old_path, const char *new_path) {
return mac_smack_fix(new_path, 0); return mac_smack_fix(new_path, 0);
} }
int symlink_atomic_label(const char *from, const char *to) {
int r;
assert(from);
assert(to);
r = mac_selinux_create_file_prepare(to, S_IFLNK);
if (r < 0)
return r;
if (symlink_atomic(from, to) < 0)
r = -errno;
mac_selinux_create_file_clear();
if (r < 0)
return r;
return mac_smack_fix(to, 0);
}
int mknod_label(const char *pathname, mode_t mode, dev_t dev) { int mknod_label(const char *pathname, mode_t mode, dev_t dev) {
int r; int r;

View File

@ -17,7 +17,6 @@ static inline int label_fix(const char *path, LabelFixFlags flags) {
int mkdir_label(const char *path, mode_t mode); int mkdir_label(const char *path, mode_t mode);
int mkdirat_label(int dirfd, const char *path, mode_t mode); int mkdirat_label(int dirfd, const char *path, mode_t mode);
int symlink_label(const char *old_path, const char *new_path); int symlink_label(const char *old_path, const char *new_path);
int symlink_atomic_label(const char *from, const char *to);
int mknod_label(const char *pathname, mode_t mode, dev_t dev); int mknod_label(const char *pathname, mode_t mode, dev_t dev);
int btrfs_subvol_make_label(const char *path); int btrfs_subvol_make_label(const char *path);

View File

@ -4,27 +4,6 @@
#include "ordered-set.h" #include "ordered-set.h"
#include "strv.h" #include "strv.h"
int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) {
if (*s)
return 0;
*s = _ordered_set_new(ops HASHMAP_DEBUG_PASS_ARGS);
if (!*s)
return -ENOMEM;
return 0;
}
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS) {
int r;
r = _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return ordered_set_put(*s, p);
}
int ordered_set_consume(OrderedSet *s, void *p) { int ordered_set_consume(OrderedSet *s, void *p) {
int r; int r;

View File

@ -7,16 +7,20 @@
typedef struct OrderedSet OrderedSet; typedef struct OrderedSet OrderedSet;
static inline OrderedSet* _ordered_set_new(const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) { static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) {
return (OrderedSet*) _ordered_hashmap_new(ops HASHMAP_DEBUG_PASS_ARGS); return (OrderedSet*) ordered_hashmap_new(ops);
} }
#define ordered_set_new(ops) _ordered_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS); static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) {
#define ordered_set_ensure_allocated(s, ops) _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_SRC_ARGS) if (*s)
return 0;
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); *s = ordered_set_new(ops);
#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) if (!*s)
return -ENOMEM;
return 0;
}
static inline OrderedSet* ordered_set_free(OrderedSet *s) { static inline OrderedSet* ordered_set_free(OrderedSet *s) {
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);

View File

@ -77,9 +77,7 @@ static inline unsigned set_buckets(const Set *s) {
return _hashmap_buckets(HASHMAP_BASE((Set *) s)); return _hashmap_buckets(HASHMAP_BASE((Set *) s));
} }
static inline bool set_iterate(const Set *s, Iterator *i, void **value) { bool set_iterate(const Set *s, Iterator *i, void **value);
return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
}
static inline void set_clear(Set *s) { static inline void set_clear(Set *s) {
_hashmap_clear(HASHMAP_BASE(s), NULL, NULL); _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);

View File

@ -150,22 +150,18 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
return true; return true;
} }
char *utf8_is_valid_n(const char *str, size_t len_bytes) { char *utf8_is_valid(const char *str) {
/* Check if the string is composed of valid utf8 characters. If length len_bytes is given, stop after const char *p;
* len_bytes. Otherwise, stop at NUL. */
assert(str); assert(str);
for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) { p = str;
while (*p) {
int len; int len;
if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1) len = utf8_encoded_valid_unichar(p, (size_t) -1);
return NULL; /* embedded NUL */ if (len < 0)
return NULL;
len = utf8_encoded_valid_unichar(p,
len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1);
if (_unlikely_(len < 0))
return NULL; /* invalid character */
p += len; p += len;
} }

View File

@ -14,10 +14,7 @@
bool unichar_is_valid(char32_t c); bool unichar_is_valid(char32_t c);
char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_; char *utf8_is_valid(const char *s) _pure_;
static inline char *utf8_is_valid(const char *s) {
return utf8_is_valid_n(s, (size_t) -1);
}
char *ascii_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len); char *ascii_is_valid_n(const char *str, size_t len);

View File

@ -291,7 +291,7 @@ int manager_varlink_init(Manager *m) {
return log_error_errno(r, "Failed to register varlink methods: %m"); return log_error_errno(r, "Failed to register varlink methods: %m");
if (!MANAGER_IS_TEST_RUN(m)) { if (!MANAGER_IS_TEST_RUN(m)) {
(void) mkdir_p_label("/run/systemd/userdb", 0755); (void) mkdir_p("/run/systemd/userdb", 0755);
r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.DynamicUser", 0666); r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
if (r < 0) if (r < 0)

View File

@ -86,7 +86,6 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FlushPending", "b", bus_property_get_bool, offsetof(Socket, flush_pending), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST),
@ -180,9 +179,6 @@ static int bus_socket_set_transient_property(
if (streq(name, "Accept")) if (streq(name, "Accept"))
return bus_set_transient_bool(u, name, &s->accept, message, flags, error); return bus_set_transient_bool(u, name, &s->accept, message, flags, error);
if (streq(name, "FlushPending"))
return bus_set_transient_bool(u, name, &s->flush_pending, message, flags, error);
if (streq(name, "Writable")) if (streq(name, "Writable"))
return bus_set_transient_bool(u, name, &s->writable, message, flags, error); return bus_set_transient_bool(u, name, &s->writable, message, flags, error);

View File

@ -1077,42 +1077,26 @@ static int enforce_groups(gid_t gid, const gid_t *supplementary_gids, int ngids)
return 0; return 0;
} }
static int set_securebits(int bits, int mask) {
int current, applied;
current = prctl(PR_GET_SECUREBITS);
if (current < 0)
return -errno;
/* Clear all securebits defined in mask and set bits */
applied = (current & ~mask) | bits;
if (current == applied)
return 0;
if (prctl(PR_SET_SECUREBITS, applied) < 0)
return -errno;
return 1;
}
static int enforce_user(const ExecContext *context, uid_t uid) { static int enforce_user(const ExecContext *context, uid_t uid) {
assert(context); assert(context);
int r;
if (!uid_is_valid(uid)) if (!uid_is_valid(uid))
return 0; return 0;
/* Sets (but doesn't look up) the uid and make sure we keep the /* Sets (but doesn't look up) the uid and make sure we keep the
* capabilities while doing so. For setting secure bits the capability CAP_SETPCAP is * capabilities while doing so. */
* required, so we also need keep-caps in this case.
*/
if (context->capability_ambient_set != 0 || context->secure_bits != 0) { if (context->capability_ambient_set != 0) {
/* First step: If we need to keep capabilities but /* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our * drop privileges we need to make sure we keep our
* caps, while we drop privileges. */ * caps, while we drop privileges. */
if (uid != 0) { if (uid != 0) {
/* Add KEEP_CAPS to the securebits */ int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
r = set_securebits(1<<SECURE_KEEP_CAPS, 0);
if (r < 0) if (prctl(PR_GET_SECUREBITS) != sb)
return r; if (prctl(PR_SET_SECUREBITS, sb) < 0)
return -errno;
} }
} }
@ -4353,27 +4337,12 @@ static int exec_child(
#endif #endif
/* PR_GET_SECUREBITS is not privileged, while PR_SET_SECUREBITS is. So to suppress potential EPERMs /* PR_GET_SECUREBITS is not privileged, while PR_SET_SECUREBITS is. So to suppress potential EPERMs
* we'll try not to call PR_SET_SECUREBITS unless necessary. Setting securebits requires * we'll try not to call PR_SET_SECUREBITS unless necessary. */
* CAP_SETPCAP. */ if (prctl(PR_GET_SECUREBITS) != secure_bits)
if (prctl(PR_GET_SECUREBITS) != secure_bits) {
/* CAP_SETPCAP is required to set securebits. This capabilitiy is raised into the
* effective set here.
* The effective set is overwritten during execve with the following values:
* - ambient set (for non-root processes)
* - (inheritable | bounding) set for root processes)
*
* Hence there is no security impact to raise it in the effective set before execve
*/
r = capability_gain_cap_setpcap(NULL);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return log_unit_error_errno(unit, r, "Failed to gain CAP_SETPCAP for setting secure bits");
}
if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) { if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS; *exit_status = EXIT_SECUREBITS;
return log_unit_error_errno(unit, errno, "Failed to set process secure bits: %m"); return log_unit_error_errno(unit, errno, "Failed to set process secure bits: %m");
} }
}
if (context_has_no_new_privileges(context)) if (context_has_no_new_privileges(context))
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {

View File

@ -391,7 +391,6 @@ Socket.SocketGroup, config_parse_user_group_compat, 0,
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
Socket.FlushPending, config_parse_bool, 0, offsetof(Socket, flush_pending)
Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source) Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source)

View File

@ -63,7 +63,6 @@
#include "ratelimit.h" #include "ratelimit.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "rm-rf.h" #include "rm-rf.h"
#include "selinux-util.h"
#include "serialize.h" #include "serialize.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h" #include "socket-util.h"
@ -964,9 +963,9 @@ static int manager_setup_notify(Manager *m) {
(void) mkdir_parents_label(m->notify_socket, 0755); (void) mkdir_parents_label(m->notify_socket, 0755);
(void) sockaddr_un_unlink(&sa.un); (void) sockaddr_un_unlink(&sa.un);
r = mac_selinux_bind(fd, &sa.sa, sa_len); r = bind(fd, &sa.sa, sa_len);
if (r < 0) if (r < 0)
return log_error_errno(r, "bind(%s) failed: %m", m->notify_socket); return log_error_errno(errno, "bind(%s) failed: %m", m->notify_socket);
r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true); r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0) if (r < 0)

View File

@ -72,7 +72,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static void flush_ports(Socket *s);
static void socket_init(Unit *u) { static void socket_init(Unit *u) {
Socket *s = SOCKET(u); Socket *s = SOCKET(u);
@ -670,11 +669,6 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, s->n_connections, prefix, s->n_connections,
prefix, s->max_connections, prefix, s->max_connections,
prefix, s->max_connections_per_source); prefix, s->max_connections_per_source);
else
fprintf(f,
"%sFlushPending: %s\n",
prefix, yes_no(s->flush_pending));
if (s->priority >= 0) if (s->priority >= 0)
fprintf(f, fprintf(f,
@ -2207,11 +2201,6 @@ static void socket_enter_listening(Socket *s) {
int r; int r;
assert(s); assert(s);
if (!s->accept && s->flush_pending) {
log_unit_debug(UNIT(s), "Flushing socket before listening.");
flush_ports(s);
}
r = socket_watch_fds(s); r = socket_watch_fds(s);
if (r < 0) { if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");

View File

@ -110,7 +110,6 @@ struct Socket {
bool accept; bool accept;
bool remove_on_stop; bool remove_on_stop;
bool writable; bool writable;
bool flush_pending;
int socket_protocol; int socket_protocol;

View File

@ -24,10 +24,10 @@
#include "fileio-label.h" #include "fileio-label.h"
#include "fileio.h" #include "fileio.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h"
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#include "install.h" #include "install.h"
#include "label.h"
#include "load-dropin.h" #include "load-dropin.h"
#include "load-fragment.h" #include "load-fragment.h"
#include "log.h" #include "log.h"
@ -5603,7 +5603,7 @@ static int unit_export_invocation_id(Unit *u) {
if (r < 0) if (r < 0)
return log_unit_debug_errno(u, r, "Failed to get invocation path: %m"); return log_unit_debug_errno(u, r, "Failed to get invocation path: %m");
r = symlink_atomic_label(u->invocation_id_string, p); r = symlink_atomic(u->invocation_id_string, p);
if (r < 0) if (r < 0)
return log_unit_debug_errno(u, r, "Failed to create invocation ID symlink %s: %m", p); return log_unit_debug_errno(u, r, "Failed to create invocation ID symlink %s: %m", p);

View File

@ -6,13 +6,13 @@
#include "env-util.h" #include "env-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "group-record-nss.h"
#include "macro.h" #include "macro.h"
#include "nss-systemd.h" #include "nss-systemd.h"
#include "nss-util.h" #include "nss-util.h"
#include "pthread-util.h" #include "pthread-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "strv.h" #include "strv.h"
#include "user-record-nss.h"
#include "user-util.h" #include "user-util.h"
#include "userdb-glue.h" #include "userdb-glue.h"
#include "userdb.h" #include "userdb.h"

View File

@ -2,9 +2,9 @@
#include "env-util.h" #include "env-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "group-record-nss.h"
#include "nss-systemd.h" #include "nss-systemd.h"
#include "strv.h" #include "strv.h"
#include "user-record-nss.h"
#include "user-record.h" #include "user-record.h"
#include "userdb-glue.h" #include "userdb-glue.h"
#include "userdb.h" #include "userdb.h"

View File

@ -1956,7 +1956,6 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
int r; int r;
if (STR_IN_SET(field, "Accept", if (STR_IN_SET(field, "Accept",
"FlushPending",
"Writable", "Writable",
"KeepAlive", "KeepAlive",
"NoDelay", "NoDelay",

View File

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "errno-util.h"
#include "group-record-nss.h"
#include "libcrypt-util.h"
#include "strv.h"
int nss_group_to_group_record(
const struct group *grp,
const struct sgrp *sgrp,
GroupRecord **ret) {
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
int r;
assert(grp);
assert(ret);
if (isempty(grp->gr_name))
return -EINVAL;
if (sgrp && !streq_ptr(sgrp->sg_namp, grp->gr_name))
return -EINVAL;
g = group_record_new();
if (!g)
return -ENOMEM;
g->group_name = strdup(grp->gr_name);
if (!g->group_name)
return -ENOMEM;
g->members = strv_copy(grp->gr_mem);
if (!g->members)
return -ENOMEM;
g->gid = grp->gr_gid;
if (sgrp) {
if (hashed_password_valid(sgrp->sg_passwd)) {
g->hashed_password = strv_new(sgrp->sg_passwd);
if (!g->hashed_password)
return -ENOMEM;
}
r = strv_extend_strv(&g->members, sgrp->sg_mem, 1);
if (r < 0)
return r;
g->administrators = strv_copy(sgrp->sg_adm);
if (!g->administrators)
return -ENOMEM;
}
r = json_build(&g->json, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name)),
JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(g->gid)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", JSON_BUILD_STRV(g->members)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(g->hashed_password)))),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->administrators), "administrators", JSON_BUILD_STRV(g->administrators))));
if (r < 0)
return r;
g->mask = USER_RECORD_REGULAR |
(!strv_isempty(g->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
*ret = TAKE_PTR(g);
return 0;
}
int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer) {
size_t buflen = 4096;
int r;
assert(grp);
assert(ret_sgrp);
assert(ret_buffer);
for (;;) {
_cleanup_free_ char *buf = NULL;
struct sgrp sgrp, *result;
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getsgnam_r(grp->gr_name, &sgrp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
*ret_sgrp = *result;
*ret_buffer = TAKE_PTR(buf);
return 0;
}
if (r < 0)
return -EIO; /* Weird, this should not return negative! */
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
}
int nss_group_record_by_name(
const char *name,
bool with_shadow,
GroupRecord **ret) {
_cleanup_free_ char *buf = NULL, *sbuf = NULL;
struct group grp, *result;
bool incomplete = false;
size_t buflen = 4096;
struct sgrp sgrp, *sresult = NULL;
int r;
assert(name);
assert(ret);
for (;;) {
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getgrnam_r(name, &grp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
break;
}
if (r < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrnam_r() returned a negative value");
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
if (with_shadow) {
r = nss_sgrp_for_group(result, &sgrp, &sbuf);
if (r < 0) {
log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
incomplete = ERRNO_IS_PRIVILEGE(r);
} else
sresult = &sgrp;
} else
incomplete = true;
r = nss_group_to_group_record(result, sresult, ret);
if (r < 0)
return r;
(*ret)->incomplete = incomplete;
return 0;
}
int nss_group_record_by_gid(
gid_t gid,
bool with_shadow,
GroupRecord **ret) {
_cleanup_free_ char *buf = NULL, *sbuf = NULL;
struct group grp, *result;
bool incomplete = false;
size_t buflen = 4096;
struct sgrp sgrp, *sresult = NULL;
int r;
assert(ret);
for (;;) {
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getgrgid_r(gid, &grp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
break;
}
if (r < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrgid_r() returned a negative value");
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
if (with_shadow) {
r = nss_sgrp_for_group(result, &sgrp, &sbuf);
if (r < 0) {
log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
incomplete = ERRNO_IS_PRIVILEGE(r);
} else
sresult = &sgrp;
} else
incomplete = true;
r = nss_group_to_group_record(result, sresult, ret);
if (r < 0)
return r;
(*ret)->incomplete = incomplete;
return 0;
}

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <grp.h>
#include <gshadow.h>
#include "group-record.h"
/* Synthesize GroupRecord objects from NSS data */
int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret);
int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer);
int nss_group_record_by_name(const char *name, bool with_shadow, GroupRecord **ret);
int nss_group_record_by_gid(gid_t gid, bool with_shadow, GroupRecord **ret);

View File

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "format-util.h"
#include "group-record-show.h"
#include "strv.h"
#include "user-util.h"
#include "userdb.h"
void group_record_show(GroupRecord *gr, bool show_full_user_info) {
int r;
printf(" Group name: %s\n",
group_record_group_name_and_realm(gr));
printf(" Disposition: %s\n", user_disposition_to_string(group_record_disposition(gr)));
if (gr->last_change_usec != USEC_INFINITY) {
char buf[FORMAT_TIMESTAMP_MAX];
printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), gr->last_change_usec));
}
if (gid_is_valid(gr->gid))
printf(" GID: " GID_FMT "\n", gr->gid);
if (show_full_user_info) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
r = membershipdb_by_group(gr->group_name, 0, &iterator);
if (r < 0) {
errno = -r;
printf(" Members: (can't acquire: %m)");
} else {
const char *prefix = " Members:";
for (;;) {
_cleanup_free_ char *user = NULL;
r = membershipdb_iterator_get(iterator, &user, NULL);
if (r == -ESRCH)
break;
if (r < 0) {
errno = -r;
printf("%s (can't iterate: %m\n", prefix);
break;
}
printf("%s %s\n", prefix, user);
prefix = " ";
}
}
} else {
const char *prefix = " Members:";
char **i;
STRV_FOREACH(i, gr->members) {
printf("%s %s\n", prefix, *i);
prefix = " ";
}
}
if (!strv_isempty(gr->administrators)) {
const char *prefix = " Admins:";
char **i;
STRV_FOREACH(i, gr->administrators) {
printf("%s %s\n", prefix, *i);
prefix = " ";
}
}
if (gr->description && !streq(gr->description, gr->group_name))
printf(" Description: %s\n", gr->description);
if (!strv_isempty(gr->hashed_password))
printf(" Passwords: %zu\n", strv_length(gr->hashed_password));
if (gr->service)
printf(" Service: %s\n", gr->service);
}

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "group-record.h"
void group_record_show(GroupRecord *gr, bool show_full_user_info);

View File

@ -405,9 +405,6 @@ int json_variant_new_stringn(JsonVariant **ret, const char *s, size_t n) {
return 0; return 0;
} }
if (!utf8_is_valid_n(s, n)) /* JSON strings must be valid UTF-8 */
return -EUCLEAN;
r = json_variant_new(&v, JSON_VARIANT_STRING, n + 1); r = json_variant_new(&v, JSON_VARIANT_STRING, n + 1);
if (r < 0) if (r < 0)
return r; return r;
@ -639,13 +636,9 @@ int json_variant_new_array_strv(JsonVariant **ret, char **l) {
return r; return r;
w->is_reference = true; w->is_reference = true;
} else { } else
if (!utf8_is_valid_n(l[v->n_elements], k)) /* JSON strings must be valid UTF-8 */
return -EUCLEAN;
memcpy(w->string, l[v->n_elements], k+1); memcpy(w->string, l[v->n_elements], k+1);
} }
}
v->normalized = true; v->normalized = true;
@ -1489,58 +1482,6 @@ static int print_source(FILE *f, JsonVariant *v, JsonFormatFlags flags, bool whi
return 0; return 0;
} }
static void json_format_string(FILE *f, const char *q, JsonFormatFlags flags) {
assert(q);
fputc('"', f);
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_GREEN, f);
for (; *q; q++)
switch (*q) {
case '"':
fputs("\\\"", f);
break;
case '\\':
fputs("\\\\", f);
break;
case '\b':
fputs("\\b", f);
break;
case '\f':
fputs("\\f", f);
break;
case '\n':
fputs("\\n", f);
break;
case '\r':
fputs("\\r", f);
break;
case '\t':
fputs("\\t", f);
break;
default:
if ((signed char) *q >= 0 && *q < ' ')
fprintf(f, "\\u%04x", *q);
else
fputc(*q, f);
break;
}
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_NORMAL, f);
fputc('"', f);
}
static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const char *prefix) { static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const char *prefix) {
int r; int r;
@ -1613,10 +1554,62 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
fputs(ANSI_NORMAL, f); fputs(ANSI_NORMAL, f);
break; break;
case JSON_VARIANT_STRING: case JSON_VARIANT_STRING: {
json_format_string(f, json_variant_string(v), flags); const char *q;
fputc('"', f);
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_GREEN, f);
for (q = json_variant_string(v); *q; q++) {
switch (*q) {
case '"':
fputs("\\\"", f);
break; break;
case '\\':
fputs("\\\\", f);
break;
case '\b':
fputs("\\b", f);
break;
case '\f':
fputs("\\f", f);
break;
case '\n':
fputs("\\n", f);
break;
case '\r':
fputs("\\r", f);
break;
case '\t':
fputs("\\t", f);
break;
default:
if ((signed char) *q >= 0 && *q < ' ')
fprintf(f, "\\u%04x", *q);
else
fputc(*q, f);
break;
}
}
if (flags & JSON_FORMAT_COLOR)
fputs(ANSI_NORMAL, f);
fputc('"', f);
break;
}
case JSON_VARIANT_ARRAY: { case JSON_VARIANT_ARRAY: {
size_t i, n; size_t i, n;

View File

@ -74,18 +74,13 @@ int make_salt(char **ret) {
#endif #endif
} }
bool looks_like_hashed_password(const char *s) { bool hashed_password_valid(const char *s) {
/* Returns false if the specified string is certainly not a hashed UNIX password. crypt(5) lists
* various hashing methods. We only reject (return false) strings which are documented to have /* Returns true if the specified string is a 'valid' hashed UNIX password, i.e. if starts with '$' or
* different meanings. * with '!$' (the latter being a valid, yet locked password). */
*
* In particular, we allow locked passwords, i.e. strings starting with "!", including just "!", if (isempty(s))
* i.e. the locked empty password. See also fc58c0c7bf7e4f525b916e3e5be0de2307fef04e.
*/
if (!s)
return false; return false;
s += strspn(s, "!"); /* Skip (possibly duplicated) locking prefix */ return STARTSWITH_SET(s, "$", "!$");
return !STR_IN_SET(s, "x", "*");
} }

View File

@ -19,4 +19,4 @@
int make_salt(char **ret); int make_salt(char **ret);
bool looks_like_hashed_password(const char *s); bool hashed_password_valid(const char *s);

View File

@ -113,6 +113,10 @@ shared_sources = files('''
geneve-util.h geneve-util.h
gpt.c gpt.c
gpt.h gpt.h
group-record-nss.c
group-record-nss.h
group-record-show.c
group-record-show.h
group-record.c group-record.c
group-record.h group-record.h
id128-print.c id128-print.c

View File

@ -6,35 +6,10 @@
#include "strv.h" #include "strv.h"
#include "user-record-nss.h" #include "user-record-nss.h"
#include "user-util.h" #include "user-util.h"
#include "utf8.h"
#define SET_IF(field, condition, value, fallback) \ #define SET_IF(field, condition, value, fallback) \
field = (condition) ? (value) : (fallback) field = (condition) ? (value) : (fallback)
static inline const char* utf8_only(const char *s) {
return s && utf8_is_valid(s) ? s : NULL;
}
static inline int strv_extend_strv_utf8_only(char ***dst, char **src, bool filter_duplicates) {
_cleanup_free_ char **t = NULL;
size_t l, j = 0;
/* First, do a shallow copy of s, filtering for only valid utf-8 strings */
l = strv_length(src);
t = new(char*, l + 1);
if (!t)
return -ENOMEM;
for (size_t i = 0; i < l; i++)
if (utf8_is_valid(src[i]))
t[j++] = src[i];
if (j == 0)
return 0;
t[j] = NULL;
return strv_extend_strv(dst, t, filter_duplicates);
}
int nss_passwd_to_user_record( int nss_passwd_to_user_record(
const struct passwd *pwd, const struct passwd *pwd,
const struct spwd *spwd, const struct spwd *spwd,
@ -80,19 +55,18 @@ int nss_passwd_to_user_record(
free_and_replace(hr->real_name, mangled); free_and_replace(hr->real_name, mangled);
} }
r = free_and_strdup(&hr->home_directory, utf8_only(empty_to_null(pwd->pw_dir))); r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir));
if (r < 0) if (r < 0)
return r; return r;
r = free_and_strdup(&hr->shell, utf8_only(empty_to_null(pwd->pw_shell))); r = free_and_strdup(&hr->shell, empty_to_null(pwd->pw_shell));
if (r < 0) if (r < 0)
return r; return r;
hr->uid = pwd->pw_uid; hr->uid = pwd->pw_uid;
hr->gid = pwd->pw_gid; hr->gid = pwd->pw_gid;
if (spwd && if (spwd && hashed_password_valid(spwd->sp_pwdp)) {
looks_like_hashed_password(utf8_only(spwd->sp_pwdp))) { /* Ignore locked, disabled, and mojibake passwords */
strv_free_erase(hr->hashed_password); strv_free_erase(hr->hashed_password);
hr->hashed_password = strv_new(spwd->sp_pwdp); hr->hashed_password = strv_new(spwd->sp_pwdp);
if (!hr->hashed_password) if (!hr->hashed_password)
@ -316,216 +290,3 @@ int nss_user_record_by_uid(
(*ret)->incomplete = incomplete; (*ret)->incomplete = incomplete;
return 0; return 0;
} }
int nss_group_to_group_record(
const struct group *grp,
const struct sgrp *sgrp,
GroupRecord **ret) {
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
int r;
assert(grp);
assert(ret);
if (isempty(grp->gr_name))
return -EINVAL;
if (sgrp && !streq_ptr(sgrp->sg_namp, grp->gr_name))
return -EINVAL;
g = group_record_new();
if (!g)
return -ENOMEM;
g->group_name = strdup(grp->gr_name);
if (!g->group_name)
return -ENOMEM;
r = strv_extend_strv_utf8_only(&g->members, grp->gr_mem, false);
if (r < 0)
return r;
g->gid = grp->gr_gid;
if (sgrp) {
if (looks_like_hashed_password(utf8_only(sgrp->sg_passwd))) {
g->hashed_password = strv_new(sgrp->sg_passwd);
if (!g->hashed_password)
return -ENOMEM;
}
r = strv_extend_strv_utf8_only(&g->members, sgrp->sg_mem, true);
if (r < 0)
return r;
r = strv_extend_strv_utf8_only(&g->administrators, sgrp->sg_adm, false);
if (r < 0)
return r;
}
r = json_build(&g->json, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name)),
JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(g->gid)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", JSON_BUILD_STRV(g->members)),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(g->hashed_password)))),
JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->administrators), "administrators", JSON_BUILD_STRV(g->administrators))));
if (r < 0)
return r;
g->mask = USER_RECORD_REGULAR |
(!strv_isempty(g->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
*ret = TAKE_PTR(g);
return 0;
}
int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer) {
size_t buflen = 4096;
int r;
assert(grp);
assert(ret_sgrp);
assert(ret_buffer);
for (;;) {
_cleanup_free_ char *buf = NULL;
struct sgrp sgrp, *result;
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getsgnam_r(grp->gr_name, &sgrp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
*ret_sgrp = *result;
*ret_buffer = TAKE_PTR(buf);
return 0;
}
if (r < 0)
return -EIO; /* Weird, this should not return negative! */
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
}
int nss_group_record_by_name(
const char *name,
bool with_shadow,
GroupRecord **ret) {
_cleanup_free_ char *buf = NULL, *sbuf = NULL;
struct group grp, *result;
bool incomplete = false;
size_t buflen = 4096;
struct sgrp sgrp, *sresult = NULL;
int r;
assert(name);
assert(ret);
for (;;) {
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getgrnam_r(name, &grp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
break;
}
if (r < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrnam_r() returned a negative value");
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
if (with_shadow) {
r = nss_sgrp_for_group(result, &sgrp, &sbuf);
if (r < 0) {
log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
incomplete = ERRNO_IS_PRIVILEGE(r);
} else
sresult = &sgrp;
} else
incomplete = true;
r = nss_group_to_group_record(result, sresult, ret);
if (r < 0)
return r;
(*ret)->incomplete = incomplete;
return 0;
}
int nss_group_record_by_gid(
gid_t gid,
bool with_shadow,
GroupRecord **ret) {
_cleanup_free_ char *buf = NULL, *sbuf = NULL;
struct group grp, *result;
bool incomplete = false;
size_t buflen = 4096;
struct sgrp sgrp, *sresult = NULL;
int r;
assert(ret);
for (;;) {
buf = malloc(buflen);
if (!buf)
return -ENOMEM;
r = getgrgid_r(gid, &grp, buf, buflen, &result);
if (r == 0) {
if (!result)
return -ESRCH;
break;
}
if (r < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrgid_r() returned a negative value");
if (r != ERANGE)
return -r;
if (buflen > SIZE_MAX / 2)
return -ERANGE;
buflen *= 2;
buf = mfree(buf);
}
if (with_shadow) {
r = nss_sgrp_for_group(result, &sgrp, &sbuf);
if (r < 0) {
log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
incomplete = ERRNO_IS_PRIVILEGE(r);
} else
sresult = &sgrp;
} else
incomplete = true;
r = nss_group_to_group_record(result, sresult, ret);
if (r < 0)
return r;
(*ret)->incomplete = incomplete;
return 0;
}

View File

@ -1,24 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1+ */ /* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once #pragma once
#include <grp.h>
#include <gshadow.h>
#include <pwd.h> #include <pwd.h>
#include <shadow.h> #include <shadow.h>
#include "group-record.h"
#include "user-record.h" #include "user-record.h"
/* Synthesize UserRecord and GroupRecord objects from NSS data */ /* Synthesizes a UserRecord object from NSS data */
int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret); int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret);
int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer); int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer);
int nss_user_record_by_name(const char *name, bool with_shadow, UserRecord **ret); int nss_user_record_by_name(const char *name, bool with_shadow, UserRecord **ret);
int nss_user_record_by_uid(uid_t uid, bool with_shadow, UserRecord **ret); int nss_user_record_by_uid(uid_t uid, bool with_shadow, UserRecord **ret);
int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret);
int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer);
int nss_group_record_by_name(const char *name, bool with_shadow, GroupRecord **ret);
int nss_group_record_by_gid(gid_t gid, bool with_shadow, GroupRecord **ret);

View File

@ -2,6 +2,7 @@
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "group-record.h"
#include "process-util.h" #include "process-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "strv.h" #include "strv.h"
@ -505,75 +506,3 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
if (hr->service) if (hr->service)
printf(" Service: %s\n", hr->service); printf(" Service: %s\n", hr->service);
} }
void group_record_show(GroupRecord *gr, bool show_full_user_info) {
int r;
printf(" Group name: %s\n",
group_record_group_name_and_realm(gr));
printf(" Disposition: %s\n", user_disposition_to_string(group_record_disposition(gr)));
if (gr->last_change_usec != USEC_INFINITY) {
char buf[FORMAT_TIMESTAMP_MAX];
printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), gr->last_change_usec));
}
if (gid_is_valid(gr->gid))
printf(" GID: " GID_FMT "\n", gr->gid);
if (show_full_user_info) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
r = membershipdb_by_group(gr->group_name, 0, &iterator);
if (r < 0) {
errno = -r;
printf(" Members: (can't acquire: %m)");
} else {
const char *prefix = " Members:";
for (;;) {
_cleanup_free_ char *user = NULL;
r = membershipdb_iterator_get(iterator, &user, NULL);
if (r == -ESRCH)
break;
if (r < 0) {
errno = -r;
printf("%s (can't iterate: %m\n", prefix);
break;
}
printf("%s %s\n", prefix, user);
prefix = " ";
}
}
} else {
const char *prefix = " Members:";
char **i;
STRV_FOREACH(i, gr->members) {
printf("%s %s\n", prefix, *i);
prefix = " ";
}
}
if (!strv_isempty(gr->administrators)) {
const char *prefix = " Admins:";
char **i;
STRV_FOREACH(i, gr->administrators) {
printf("%s %s\n", prefix, *i);
prefix = " ";
}
}
if (gr->description && !streq(gr->description, gr->group_name))
printf(" Description: %s\n", gr->description);
if (!strv_isempty(gr->hashed_password))
printf(" Passwords: %zu\n", strv_length(gr->hashed_password));
if (gr->service)
printf(" Service: %s\n", gr->service);
}

View File

@ -2,9 +2,7 @@
#pragma once #pragma once
#include "user-record.h" #include "user-record.h"
#include "group-record.h"
const char *user_record_state_color(const char *state); const char *user_record_state_color(const char *state);
void user_record_show(UserRecord *hr, bool show_full_group_info); void user_record_show(UserRecord *hr, bool show_full_group_info);
void group_record_show(GroupRecord *gr, bool show_full_user_info);

View File

@ -6,6 +6,7 @@
#include "dlfcn-util.h" #include "dlfcn-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "group-record-nss.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "parse-util.h" #include "parse-util.h"
#include "set.h" #include "set.h"

View File

@ -9,7 +9,6 @@
#include "io-util.h" #include "io-util.h"
#include "list.h" #include "list.h"
#include "process-util.h" #include "process-util.h"
#include "selinux-util.h"
#include "set.h" #include "set.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-table.h" #include "string-table.h"
@ -580,17 +579,11 @@ static int varlink_parse_message(Varlink *v) {
sz = e - begin + 1; sz = e - begin + 1;
varlink_log(v, "New incoming message: %s", begin); /* FIXME: should we output the whole message here before validation? varlink_log(v, "New incoming message: %s", begin);
* This may produce a non-printable journal entry if the message
* is invalid. We may also expose privileged information. */
r = json_parse(begin, 0, &v->current, NULL, NULL); r = json_parse(begin, 0, &v->current, NULL, NULL);
if (r < 0) { if (r < 0)
/* If we encounter a parse failure flush all data. We cannot possibly recover from this, return r;
* hence drop all buffered data now. */
v->input_buffer_index = v->input_buffer_size = v->input_buffer_unscanned = 0;
return varlink_log_errno(v, r, "Failed to parse JSON: %m");
}
v->input_buffer_size -= sz; v->input_buffer_size -= sz;
@ -2250,11 +2243,9 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
(void) sockaddr_un_unlink(&sockaddr.un); (void) sockaddr_un_unlink(&sockaddr.un);
RUN_WITH_UMASK(~m & 0777) { RUN_WITH_UMASK(~m & 0777)
r = mac_selinux_bind(fd, &sockaddr.sa, sockaddr_len); if (bind(fd, &sockaddr.sa, sockaddr_len) < 0)
if (r < 0) return -errno;
return r;
}
if (listen(fd, SOMAXCONN) < 0) if (listen(fd, SOMAXCONN) < 0)
return -errno; return -errno;

View File

@ -3,7 +3,6 @@
#include <math.h> #include <math.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "json-internal.h" #include "json-internal.h"
@ -18,10 +17,6 @@ static void test_tokenizer(const char *data, ...) {
void *state = NULL; void *state = NULL;
va_list ap; va_list ap;
_cleanup_free_ char *cdata;
assert_se(cdata = cescape(data));
log_info("/* %s data=\"%s\" */", __func__, cdata);
va_start(ap, data); va_start(ap, data);
for (;;) { for (;;) {
@ -87,10 +82,6 @@ static void test_variant(const char *data, Test test) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
int r; int r;
_cleanup_free_ char *cdata;
assert_se(cdata = cescape(data));
log_info("/* %s data=\"%s\" */", __func__, cdata);
r = json_parse(data, 0, &v, NULL, NULL); r = json_parse(data, 0, &v, NULL, NULL);
assert_se(r == 0); assert_se(r == 0);
assert_se(v); assert_se(v);
@ -149,8 +140,6 @@ static void test_1(JsonVariant *v) {
JsonVariant *p, *q; JsonVariant *p, *q;
unsigned i; unsigned i;
log_info("/* %s */", __func__);
/* 3 keys + 3 values */ /* 3 keys + 3 values */
assert_se(json_variant_elements(v) == 6); assert_se(json_variant_elements(v) == 6);
@ -184,8 +173,6 @@ static void test_1(JsonVariant *v) {
static void test_2(JsonVariant *v) { static void test_2(JsonVariant *v) {
JsonVariant *p, *q; JsonVariant *p, *q;
log_info("/* %s */", __func__);
/* 2 keys + 2 values */ /* 2 keys + 2 values */
assert_se(json_variant_elements(v) == 4); assert_se(json_variant_elements(v) == 4);
@ -229,12 +216,13 @@ static void test_2(JsonVariant *v) {
} }
static void test_zeroes(JsonVariant *v) { static void test_zeroes(JsonVariant *v) {
size_t i;
/* Make sure zero is how we expect it. */ /* Make sure zero is how we expect it. */
log_info("/* %s */", __func__);
assert_se(json_variant_elements(v) == 13); assert_se(json_variant_elements(v) == 13);
for (size_t i = 0; i < json_variant_elements(v); i++) { for (i = 0; i < json_variant_elements(v); i++) {
JsonVariant *w; JsonVariant *w;
size_t j; size_t j;
@ -267,8 +255,6 @@ static void test_zeroes(JsonVariant *v) {
} }
static void test_build(void) { static void test_build(void) {
log_info("/* %s */", __func__);
_cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL; _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL;
_cleanup_free_ char *s = NULL, *t = NULL; _cleanup_free_ char *s = NULL, *t = NULL;
@ -369,8 +355,6 @@ static void test_source(void) {
"false, 7.5, {} ]\n" "false, 7.5, {} ]\n"
"}\n"; "}\n";
log_info("/* %s */", __func__);
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
@ -392,16 +376,15 @@ static void test_source(void) {
} }
static void test_depth(void) { static void test_depth(void) {
log_info("/* %s */", __func__);
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
unsigned i;
int r; int r;
v = JSON_VARIANT_STRING_CONST("start"); v = JSON_VARIANT_STRING_CONST("start");
/* Let's verify that the maximum depth checks work */ /* Let's verify that the maximum depth checks work */
for (unsigned i = 0;; i++) { for (i = 0;; i++) {
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL; _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
assert_se(i <= UINT16_MAX); assert_se(i <= UINT16_MAX);
@ -432,8 +415,6 @@ static void test_depth(void) {
} }
static void test_normalize(void) { static void test_normalize(void) {
log_info("/* %s */", __func__);
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
@ -478,13 +459,12 @@ static void test_normalize(void) {
} }
static void test_bisect(void) { static void test_bisect(void) {
log_info("/* %s */", __func__);
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
char c;
/* Tests the bisection logic in json_variant_by_key() */ /* Tests the bisection logic in json_variant_by_key() */
for (char c = 'z'; c >= 'a'; c--) { for (c = 'z'; c >= 'a'; c--) {
if ((c % 3) == 0) if ((c % 3) == 0)
continue; continue;
@ -504,7 +484,7 @@ static void test_bisect(void) {
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
for (char c = 'a'; c <= 'z'; c++) { for (c = 'a'; c <= 'z'; c++) {
JsonVariant *k; JsonVariant *k;
const char *z; const char *z;
@ -563,7 +543,7 @@ int main(int argc, char *argv[]) {
test_variant("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1); test_variant("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
test_variant("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"thisisaverylongproperty\": 1.27}", test_2); test_variant("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"thisisaverylongproperty\": 1.27}", test_2);
test_variant("{\"foo\" : \"\\u0935\\u093f\\u0935\\u0947\\u0915\\u0916\\u094d\\u092f\\u093e\\u0924\\u093f\\u0930\\u0935\\u093f\\u092a\\u094d\\u0932\\u0935\\u093e\\u0020\\u0939\\u093e\\u0928\\u094b\\u092a\\u093e\\u092f\\u0903\\u0964\"}", NULL); test_variant("{\"foo\" : \"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFFFF\\\"\\uDBFF\\uDFFF\\\"\\uD9FF\\uDFFF\\uDBFF\\uDFFF\"}", NULL);
test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes); test_variant("[ 0, -0, 0.0, -0.0, 0.000, -0.000, 0e0, -0e0, 0e+0, -0e-0, 0e-0, -0e000, 0e+000 ]", test_zeroes);

View File

@ -18,25 +18,6 @@ static void test_utf8_is_printable(void) {
assert_se(utf8_is_printable("\t", 1)); assert_se(utf8_is_printable("\t", 1));
} }
static void test_utf8_n_is_valid(void) {
log_info("/* %s */", __func__);
assert_se( utf8_is_valid_n("ascii is valid unicode", 21));
assert_se( utf8_is_valid_n("ascii is valid unicode", 22));
assert_se(!utf8_is_valid_n("ascii is valid unicode", 23));
assert_se( utf8_is_valid_n("\342\204\242", 0));
assert_se(!utf8_is_valid_n("\342\204\242", 1));
assert_se(!utf8_is_valid_n("\342\204\242", 2));
assert_se( utf8_is_valid_n("\342\204\242", 3));
assert_se(!utf8_is_valid_n("\342\204\242", 4));
assert_se( utf8_is_valid_n("<ZZ>", 0));
assert_se( utf8_is_valid_n("<ZZ>", 1));
assert_se( utf8_is_valid_n("<ZZ>", 2));
assert_se( utf8_is_valid_n("<ZZ>", 3));
assert_se( utf8_is_valid_n("<ZZ>", 4));
assert_se(!utf8_is_valid_n("<ZZ>", 5));
}
static void test_utf8_is_valid(void) { static void test_utf8_is_valid(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
@ -235,7 +216,6 @@ static void test_utf8_to_utf16(void) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_utf8_n_is_valid();
test_utf8_is_valid(); test_utf8_is_valid();
test_utf8_is_printable(); test_utf8_is_printable();
test_ascii_is_valid(); test_ascii_is_valid();

View File

@ -8,6 +8,7 @@
#include "fd-util.h" #include "fd-util.h"
#include "format-table.h" #include "format-table.h"
#include "format-util.h" #include "format-util.h"
#include "group-record-show.h"
#include "main-func.h" #include "main-func.h"
#include "pager.h" #include "pager.h"
#include "parse-util.h" #include "parse-util.h"
@ -686,8 +687,7 @@ static int parse_argv(int argc, char *argv[]) {
else if (streq(optarg, "help")) { else if (streq(optarg, "help")) {
puts("classic\n" puts("classic\n"
"friendly\n" "friendly\n"
"json\n" "json");
"table");
return 0; return 0;
} else } else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg);

View File

@ -11,7 +11,7 @@
/* This service offers two Varlink services, both implementing io.systemd.UserDatabase: /* This service offers two Varlink services, both implementing io.systemd.UserDatabase:
* *
* io.systemd.NameServiceSwitch: this is a compatibility interface for glibc NSS: it responds to * io.systemd.NameServiceSwitch: this is a compatibility interface for glibc NSS: it response to
* name lookups by checking the classic NSS interfaces and responding that. * name lookups by checking the classic NSS interfaces and responding that.
* *
* io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a * io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a

View File

@ -7,6 +7,7 @@
#include "env-util.h" #include "env-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "group-record-nss.h"
#include "group-record.h" #include "group-record.h"
#include "io-util.h" #include "io-util.h"
#include "main-func.h" #include "main-func.h"