Coverage Report

Created: 2026-07-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/selinux/libselinux/src/seusers.c
Line
Count
Source
1
#include <unistd.h>
2
#include <fcntl.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <stdio.h>
6
#include <stdio_ext.h>
7
#include <ctype.h>
8
#include <errno.h>
9
#include <limits.h>
10
11
#include <selinux/selinux.h>
12
#include <selinux/context.h>
13
14
#include "selinux_internal.h"
15
#include "callbacks.h"
16
17
/* Process line from seusers.conf and split into its fields.
18
   Returns 0 on success, -1 on comments, and -2 on error. */
19
static int process_seusers(const char *buffer, char **luserp, char **seuserp,
20
         char **levelp, int mls_enabled)
21
0
{
22
0
  char *newbuf = strdup(buffer);
23
0
  char *luser = NULL, *seuser = NULL, *level = NULL;
24
0
  char *start, *end;
25
0
  int mls_found = 1;
26
27
0
  if (!newbuf)
28
0
    goto err;
29
30
0
  start = newbuf;
31
0
  while (isspace((unsigned char)*start))
32
0
    start++;
33
0
  if (*start == '#' || *start == 0) {
34
0
    free(newbuf);
35
0
    return -1; /* Comment or empty line, skip over */
36
0
  }
37
0
  end = strchr(start, ':');
38
0
  if (!end)
39
0
    goto err;
40
0
  *end = 0;
41
42
0
  luser = strdup(start);
43
0
  if (!luser)
44
0
    goto err;
45
46
0
  start = end + 1;
47
0
  end = strchr(start, ':');
48
0
  if (!end) {
49
0
    mls_found = 0;
50
51
0
    end = start;
52
0
    while (*end && !isspace((unsigned char)*end))
53
0
      end++;
54
0
  }
55
0
  *end = 0;
56
57
0
  seuser = strdup(start);
58
0
  if (!seuser)
59
0
    goto err;
60
61
0
  if (!strcmp(seuser, ""))
62
0
    goto err;
63
64
  /* Skip MLS if disabled, or missing. */
65
0
  if (!mls_enabled || !mls_found)
66
0
    goto out;
67
68
0
  start = ++end;
69
0
  while (*end && !isspace((unsigned char)*end))
70
0
    end++;
71
0
  *end = 0;
72
73
0
  level = strdup(start);
74
0
  if (!level)
75
0
    goto err;
76
77
0
  if (!strcmp(level, ""))
78
0
    goto err;
79
80
0
out:
81
0
  free(newbuf);
82
0
  *luserp = luser;
83
0
  *seuserp = seuser;
84
0
  *levelp = level;
85
0
  return 0;
86
0
err:
87
0
  free(newbuf);
88
0
  free(luser);
89
0
  free(seuser);
90
0
  free(level);
91
0
  return -2; /* error */
92
0
}
93
94
int require_seusers = 0;
95
96
#include <pwd.h>
97
#include <grp.h>
98
99
static gid_t get_default_gid(const char *name)
100
0
{
101
0
  struct passwd pwstorage, *pwent = NULL;
102
0
  gid_t gid = (gid_t)-1;
103
  /* Allocate space for the getpwnam_r buffer */
104
0
  char *rbuf = NULL;
105
0
  long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
106
0
  if (rbuflen <= 0)
107
0
    rbuflen = 1024;
108
109
0
  for (;;) {
110
0
    int rc;
111
112
0
    rbuf = malloc(rbuflen);
113
0
    if (rbuf == NULL)
114
0
      break;
115
116
0
    rc = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
117
0
    if (rc == ERANGE &&
118
0
        !__builtin_smull_overflow(rbuflen, 2, &rbuflen)) {
119
0
      free(rbuf);
120
0
      continue;
121
0
    }
122
0
    if (rc == 0 && pwent)
123
0
      gid = pwent->pw_gid;
124
125
0
    break;
126
0
  }
127
128
0
  free(rbuf);
129
0
  return gid;
130
0
}
131
132
static int check_group(const char *group, const char *name, const gid_t gid)
133
0
{
134
0
  int match = 0;
135
0
  int i, ng = 0;
136
0
  gid_t *groups = NULL;
137
0
  struct group gbuf, *grent = NULL;
138
139
0
  long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
140
0
  if (rbuflen <= 0)
141
0
    rbuflen = 1024;
142
0
  char *rbuf;
143
144
0
  while (1) {
145
0
    rbuf = malloc(rbuflen);
146
0
    if (rbuf == NULL)
147
0
      return 0;
148
0
    int retval = getgrnam_r(group, &gbuf, rbuf, rbuflen, &grent);
149
0
    if (retval == ERANGE && rbuflen < LONG_MAX / 2) {
150
0
      free(rbuf);
151
0
      rbuflen = rbuflen * 2;
152
0
    } else if (retval != 0 || grent == NULL) {
153
0
      goto done;
154
0
    } else {
155
0
      break;
156
0
    }
157
0
  }
158
159
0
  if (getgrouplist(name, gid, NULL, &ng) < 0) {
160
0
    if (ng == 0)
161
0
      goto done;
162
0
    groups = calloc(ng, sizeof(*groups));
163
0
    if (!groups)
164
0
      goto done;
165
0
    if (getgrouplist(name, gid, groups, &ng) < 0)
166
0
      goto done;
167
0
  } else {
168
    /* WTF?  ng was 0 and we didn't fail? Are we in 0 groups? */
169
0
    goto done;
170
0
  }
171
172
0
  for (i = 0; i < ng; i++) {
173
0
    if (grent->gr_gid == groups[i]) {
174
0
      match = 1;
175
0
      goto done;
176
0
    }
177
0
  }
178
179
0
done:
180
0
  free(groups);
181
0
  free(rbuf);
182
0
  return match;
183
0
}
184
185
int getseuserbyname(const char *name, char **r_seuser, char **r_level)
186
0
{
187
0
  FILE *cfg = NULL;
188
0
  size_t size = 0;
189
0
  char *buffer = NULL;
190
0
  int rc;
191
0
  unsigned long lineno = 0;
192
0
  int mls_enabled = is_selinux_mls_enabled();
193
194
0
  char *username = NULL;
195
0
  char *seuser = NULL;
196
0
  char *level = NULL;
197
0
  char *groupseuser = NULL;
198
0
  char *grouplevel = NULL;
199
0
  char *defaultseuser = NULL;
200
0
  char *defaultlevel = NULL;
201
202
0
  gid_t gid = get_default_gid(name);
203
204
0
  cfg = fopen(selinux_usersconf_path(), "re");
205
0
  if (!cfg)
206
0
    goto nomatch;
207
208
0
  __fsetlocking(cfg, FSETLOCKING_BYCALLER);
209
0
  while (getline(&buffer, &size, cfg) > 0) {
210
0
    ++lineno;
211
0
    rc = process_seusers(buffer, &username, &seuser, &level,
212
0
             mls_enabled);
213
0
    if (rc == -1)
214
0
      continue; /* comment, skip */
215
0
    if (rc == -2) {
216
0
      selinux_log(SELINUX_ERROR,
217
0
            "%s:  error on line %lu, skipping...\n",
218
0
            selinux_usersconf_path(), lineno);
219
0
      continue;
220
0
    }
221
222
0
    if (!strcmp(username, name))
223
0
      break;
224
225
0
    if (username[0] == '%' && !groupseuser &&
226
0
        check_group(&username[1], name, gid)) {
227
0
      groupseuser = seuser;
228
0
      grouplevel = level;
229
0
    } else {
230
0
      if (!defaultseuser &&
231
0
          !strcmp(username, "__default__")) {
232
0
        defaultseuser = seuser;
233
0
        defaultlevel = level;
234
0
      } else {
235
0
        free(seuser);
236
0
        free(level);
237
0
      }
238
0
    }
239
0
    free(username);
240
0
    username = NULL;
241
0
    seuser = NULL;
242
0
  }
243
244
0
  free(buffer);
245
0
  fclose(cfg);
246
247
0
  if (seuser) {
248
0
    free(username);
249
0
    free(defaultseuser);
250
0
    free(defaultlevel);
251
0
    free(groupseuser);
252
0
    free(grouplevel);
253
0
    *r_seuser = seuser;
254
0
    *r_level = level;
255
0
    return 0;
256
0
  }
257
258
0
  if (groupseuser) {
259
0
    free(defaultseuser);
260
0
    free(defaultlevel);
261
0
    *r_seuser = groupseuser;
262
0
    *r_level = grouplevel;
263
0
    return 0;
264
0
  }
265
266
0
  if (defaultseuser) {
267
0
    *r_seuser = defaultseuser;
268
0
    *r_level = defaultlevel;
269
0
    return 0;
270
0
  }
271
272
0
nomatch:
273
0
  if (require_seusers)
274
0
    return -1;
275
276
  /* Fall back to the Linux username and no level. */
277
0
  *r_seuser = strdup(name);
278
0
  if (!(*r_seuser))
279
0
    return -1;
280
0
  *r_level = NULL;
281
0
  return 0;
282
0
}
283
284
int getseuser(const char *username, const char *service, char **r_seuser,
285
        char **r_level)
286
0
{
287
0
  int ret = -1;
288
0
  int len = 0;
289
0
  char *seuser = NULL;
290
0
  char *level = NULL;
291
0
  char *buffer = NULL;
292
0
  size_t size = 0;
293
0
  char *rec = NULL;
294
0
  char *path = NULL;
295
0
  FILE *fp = NULL;
296
0
  if (asprintf(&path, "%s/logins/%s", selinux_policy_root(), username) <
297
0
      0)
298
0
    goto err;
299
0
  fp = fopen(path, "re");
300
0
  free(path);
301
0
  if (fp == NULL)
302
0
    goto err;
303
0
  __fsetlocking(fp, FSETLOCKING_BYCALLER);
304
0
  while (getline(&buffer, &size, fp) > 0) {
305
0
    if (strncmp(buffer, "*:", 2) == 0) {
306
0
      free(rec);
307
0
      rec = strdup(buffer);
308
0
      if (!rec)
309
0
        goto err;
310
0
      continue;
311
0
    }
312
0
    if (!service)
313
0
      continue;
314
0
    len = strlen(service);
315
0
    if ((strncmp(buffer, service, len) == 0) &&
316
0
        (buffer[len] == ':')) {
317
0
      free(rec);
318
0
      rec = strdup(buffer);
319
0
      if (!rec)
320
0
        goto err;
321
0
      break;
322
0
    }
323
0
  }
324
325
0
  if (!rec)
326
0
    goto err;
327
0
  seuser = strchr(rec, ':');
328
0
  if (!seuser)
329
0
    goto err;
330
331
0
  seuser++;
332
0
  level = strchr(seuser, ':');
333
0
  if (!level)
334
0
    goto err;
335
0
  *level = 0;
336
0
  level++;
337
0
  *r_seuser = strdup(seuser);
338
0
  if (!*r_seuser)
339
0
    goto err;
340
341
0
  len = strlen(level);
342
0
  if (len && level[len - 1] == '\n')
343
0
    level[len - 1] = 0;
344
345
0
  *r_level = strdup(level);
346
0
  if (!*r_level) {
347
0
    free(*r_seuser);
348
0
    goto err;
349
0
  }
350
0
  ret = 0;
351
352
0
err:
353
0
  free(buffer);
354
0
  if (fp)
355
0
    fclose(fp);
356
0
  free(rec);
357
358
0
  return (ret ? getseuserbyname(username, r_seuser, r_level) : ret);
359
0
}