#include "shadowlog.h"
#undef NDEBUG
-#include <assert.h>
+/*
+ * find_new_sub_gids_deterministic - Assign a subordinate GID range by UID.
+ *
+ * Calculates a deterministic subordinate GID range for a given UID based
+ * on its offset from UID_MIN. Loads SUB_GID_COUNT from login.defs and
+ * writes it back to *range_count on success.
+ *
+ * BASE FORMULA:
+ * uid_offset = uid - UID_MIN
+ * logical_offset = uid_offset * SUB_GID_COUNT
+ * start_id = SUB_GID_MIN + logical_offset
+ * end_id = start_id + SUB_GID_COUNT - 1
+ *
+ * DETERMINISTIC MODE:
+ * All arithmetic overflow is a hard error. The assigned range must fit
+ * entirely within [SUB_GID_MIN, SUB_GID_MAX]. Allocation is monotonic
+ * and guaranteed non-overlapping.
+ *
+ * Return 0 on success, -1 if no GIDs are available.
+ */
+static int
+find_new_sub_gids_deterministic(uid_t uid,
+ id_t *range_start,
+ unsigned long *range_count)
+{
+ unsigned long count;
+ unsigned long slots;
+ unsigned long space;
+ unsigned long uid_min;
+ unsigned long sub_gid_max;
+ unsigned long sub_gid_min;
+ unsigned long uid_offset;
+
+ uid_min = getdef_ulong ("UID_MIN", 1000UL);
+ sub_gid_min = getdef_ulong ("SUB_GID_MIN", 65536UL);
+ sub_gid_max = getdef_ulong ("SUB_GID_MAX", 4294967295UL);
+ count = getdef_ulong ("SUB_GID_COUNT", 65536UL);
+
+ if (uid < uid_min) {
+ fprintf(log_get_logfd(),
+ _("%s: UID %ju is less than UID_MIN %lu,"
+ " cannot calculate deterministic subordinate GIDs\n"),
+ log_get_progname(),
+ (uintmax_t)uid, uid_min);
+ return -1;
+ }
+
+ if (sub_gid_min > sub_gid_max || count == 0) {
+ fprintf(log_get_logfd(),
+ _("%s: Invalid configuration: SUB_GID_MIN (%lu),"
+ " SUB_GID_MAX (%lu), SUB_GID_COUNT (%lu)\n"),
+ log_get_progname(),
+ sub_gid_min, sub_gid_max, count);
+ return -1;
+ }
+
+ if (__builtin_add_overflow(sub_gid_max - sub_gid_min, 1UL, &space)) {
+ fprintf(log_get_logfd(),
+ _("%s: SUB_GID range [%lu, %lu] is too large"
+ " to represent\n"),
+ log_get_progname(),
+ sub_gid_min, sub_gid_max);
+ return -1;
+ }
+
+ if (count > space) {
+ fprintf(log_get_logfd(),
+ _("%s: Not enough space for any subordinate GIDs"
+ " (SUB_GID_MIN=%lu, SUB_GID_MAX=%lu,"
+ " SUB_GID_COUNT=%lu)\n"),
+ log_get_progname(),
+ sub_gid_min, sub_gid_max, count);
+ return -1;
+ }
+
+ uid_offset = uid - uid_min;
+ slots = space / count;
+
+ if (uid_offset >= slots) {
+ fprintf(log_get_logfd(),
+ _("%s: Deterministic subordinate GID range"
+ " for UID %ju exceeds SUB_GID_MAX (%lu)\n"),
+ log_get_progname(),
+ (uintmax_t)uid, sub_gid_max);
+ return -1;
+ }
+
+ *range_start = sub_gid_min + uid_offset * count;
+ *range_count = count;
+ return 0;
+}
+
/*
* find_new_sub_gids_linear - Find an unused subordinate GID range via
* linear search.
* Return 0 on success, -1 if no unused GIDs are available.
*/
int
-find_new_sub_gids(id_t *range_start, unsigned long *range_count)
+find_new_sub_gids(uid_t uid, id_t *range_start, unsigned long *range_count)
{
+ if (getdef_bool("SUB_GID_DETERMINISTIC"))
+ return find_new_sub_gids_deterministic(uid, range_start, range_count);
+
return find_new_sub_gids_linear(range_start, range_count);
}
#include "shadowlog.h"
#undef NDEBUG
-#include <assert.h>
+/*
+ * find_new_sub_uids_deterministic - Assign a subordinate UID range by UID.
+ *
+ * Calculates a deterministic subordinate UID range for a given UID based
+ * on its offset from UID_MIN. Loads SUB_UID_COUNT from login.defs and
+ * writes it back to *range_count on success.
+ *
+ * BASE FORMULA:
+ * uid_offset = uid - UID_MIN
+ * logical_offset = uid_offset * SUB_UID_COUNT
+ * start_id = SUB_UID_MIN + logical_offset
+ * end_id = start_id + SUB_UID_COUNT - 1
+ *
+ * DETERMINISTIC MODE:
+ * All arithmetic overflow is a hard error. The assigned range must fit
+ * entirely within [SUB_UID_MIN, SUB_UID_MAX]. Allocation is monotonic
+ * and guaranteed non-overlapping.
+ *
+ * Return 0 on success, -1 if no UIDs are available.
+ */
+static int
+find_new_sub_uids_deterministic(uid_t uid,
+ id_t *range_start,
+ unsigned long *range_count)
+{
+ unsigned long count;
+ unsigned long slots;
+ unsigned long space;
+ unsigned long uid_min;
+ unsigned long uid_offset;
+ unsigned long sub_uid_max;
+ unsigned long sub_uid_min;
+
+ uid_min = getdef_ulong ("UID_MIN", 1000UL);
+ sub_uid_min = getdef_ulong ("SUB_UID_MIN", 65536UL);
+ sub_uid_max = getdef_ulong ("SUB_UID_MAX", 4294967295UL);
+ count = getdef_ulong ("SUB_UID_COUNT", 65536UL);
+
+ if (uid < uid_min) {
+ fprintf(log_get_logfd(),
+ _("%s: UID %ju is less than UID_MIN %lu,"
+ " cannot calculate deterministic subordinate UIDs\n"),
+ log_get_progname(),
+ (uintmax_t)uid, uid_min);
+ return -1;
+ }
+
+ if (sub_uid_min > sub_uid_max || count == 0) {
+ fprintf(log_get_logfd(),
+ _("%s: Invalid configuration: SUB_UID_MIN (%lu),"
+ " SUB_UID_MAX (%lu), SUB_UID_COUNT (%lu)\n"),
+ log_get_progname(),
+ sub_uid_min, sub_uid_max, count);
+ return -1;
+ }
+
+ if (__builtin_add_overflow(sub_uid_max - sub_uid_min, 1UL, &space)) {
+ fprintf(log_get_logfd(),
+ _("%s: SUB_UID range [%lu, %lu] is too large"
+ " to represent\n"),
+ log_get_progname(),
+ sub_uid_min, sub_uid_max);
+ return -1;
+ }
+
+ if (count > space) {
+ fprintf(log_get_logfd(),
+ _("%s: Not enough space for any subordinate UIDs"
+ " (SUB_UID_MIN=%lu, SUB_UID_MAX=%lu,"
+ " SUB_UID_COUNT=%lu)\n"),
+ log_get_progname(),
+ sub_uid_min, sub_uid_max, count);
+ return -1;
+ }
+
+ uid_offset = uid - uid_min;
+ slots = space / count;
+
+ if (uid_offset > slots) {
+ fprintf(log_get_logfd(),
+ _("%s: Deterministic subordinate UID range"
+ " for UID %ju exceeds SUB_UID_MAX (%lu)\n"),
+ log_get_progname(),
+ (uintmax_t)uid, sub_uid_max);
+ return -1;
+ }
+
+ *range_start = sub_uid_min + uid_offset * count;
+ *range_count = count;
+ return 0;
+}
+
/*
* find_new_sub_uids_linear - Find an unused subordinate UID range via
* linear search.
* Return 0 on success, -1 if no unused UIDs are available.
*/
int
-find_new_sub_uids(id_t *range_start, unsigned long *range_count)
+find_new_sub_uids(uid_t uid, id_t *range_start, unsigned long *range_count)
{
+ if (getdef_bool("SUB_UID_DETERMINISTIC"))
+ return find_new_sub_uids_deterministic(uid, range_start, range_count);
+
return find_new_sub_uids_linear(range_start, range_count);
}
#ifdef ENABLE_SUBIDS
/* find_new_sub_gids.c */
-extern int find_new_sub_gids (id_t *range_start, unsigned long *range_count);
+extern int find_new_sub_gids(uid_t uid, id_t *range_start, unsigned long *range_count);
/* find_new_sub_uids.c */
-extern int find_new_sub_uids (id_t *range_start, unsigned long *range_count);
+extern int find_new_sub_uids(uid_t uid, id_t *range_start, unsigned long *range_count);
#endif /* ENABLE_SUBIDS */
/* getgr_nam_gid.c */
if (is_sub_uid && !local_sub_uid_assigned(fields[0])) {
uid_t sub_uid_start = 0;
unsigned long sub_uid_count = 0;
- if (find_new_sub_uids(&sub_uid_start, &sub_uid_count) != 0)
+ if (find_new_sub_uids(newpw.pw_uid, &sub_uid_start, &sub_uid_count) != 0)
{
fprintf (stderr,
_("%s: can't find subordinate user range: %s\n"),
if (is_sub_gid && !local_sub_gid_assigned(fields[0])) {
gid_t sub_gid_start = 0;
unsigned long sub_gid_count = 0;
- if (find_new_sub_gids(&sub_gid_start, &sub_gid_count) != 0) {
+ if (find_new_sub_gids(newpw.pw_uid, &sub_gid_start, &sub_gid_count) != 0) {
fprintf (stderr,
_("%s: can't find subordinate group range: %s\n"),
Prog, strerrno());
#ifdef ENABLE_SUBIDS
if (is_sub_uid && subuid_count != 0) {
- if (find_new_sub_uids(&sub_uid_start, &subuid_count) < 0) {
+ if (find_new_sub_uids(user_id, &sub_uid_start, &subuid_count) < 0) {
fprintf (stderr,
_("%s: can't create subordinate user IDs: %s\n"),
Prog, strerrno());
}
}
if (is_sub_gid && subgid_count != 0) {
- if (find_new_sub_gids(&sub_gid_start, &subgid_count) < 0) {
+ if (find_new_sub_gids(user_id, &sub_gid_start, &subgid_count) < 0) {
fprintf (stderr,
_("%s: can't create subordinate group IDs: %s\n"),
Prog, strerrno());
}
static int
-find_range(struct id_range_list_entry **head,
- int (*find_fn)(id_t *range_start, unsigned long *range_count))
+find_range(struct id_range_list_entry **head, uid_t uid,
+ int (*find_fn)(uid_t uid, id_t *range_start, unsigned long *range_count))
{
struct id_range_list_entry *entry;
struct id_range range;
unsigned long count;
- if (find_fn(&range.first, &count) != 0)
+ if (find_fn(uid, &range.first, &count) != 0)
return 0;
range.last = range.first + count;
}
#ifdef ENABLE_SUBIDS
if (Sflg) {
- if (find_range (&add_sub_uids, find_new_sub_uids) == 0) {
+ if (find_range (&add_sub_uids, user_id, find_new_sub_uids) == 0) {
fprintf (stderr,
_("%s: unable to find new subordinate uid range: %s\n"),
Prog, strerrno());
strerrno());
fail_exit (E_SUB_UID_UPDATE, process_selinux);
}
- if (find_range (&add_sub_gids, find_new_sub_gids) == 0) {
+ if (find_range (&add_sub_gids, user_id, find_new_sub_gids) == 0) {
fprintf (stderr,
_("%s: unable to find new subordinate gid range: %s\n"),
Prog, strerrno());