Coverage Report

Created: 2025-09-17 06:21

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