Coverage Report

Created: 2024-02-29 06:05

/src/strongswan/src/libstrongswan/utils/capabilities.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2012-2015 Tobias Brunner
3
 * Copyright (C) 2012 Martin Willi
4
 *
5
 * Copyright (C) secunet Security Networks AG
6
 *
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by the
9
 * Free Software Foundation; either version 2 of the License, or (at your
10
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15
 * for more details.
16
 */
17
18
#include "capabilities.h"
19
20
#include <utils/debug.h>
21
22
#include <errno.h>
23
#include <string.h>
24
#include <sys/types.h>
25
#include <unistd.h>
26
27
#ifndef WIN32
28
#include <pwd.h>
29
#include <grp.h>
30
#ifdef HAVE_PRCTL
31
# include <sys/prctl.h>
32
#endif /* HAVE_PRCTL */
33
34
#if !defined(HAVE_GETPWNAM_R) || \
35
    !defined(HAVE_GETGRNAM_R) || \
36
    !defined(HAVE_GETPWUID_R)
37
# include <threading/mutex.h>
38
# define EMULATE_R_FUNCS
39
#endif
40
#endif /* !WIN32 */
41
42
typedef struct private_capabilities_t private_capabilities_t;
43
44
/**
45
 * Private data of an capabilities_t object.
46
 */
47
struct private_capabilities_t {
48
49
  /**
50
   * Public capabilities_t interface.
51
   */
52
  capabilities_t public;
53
54
  /**
55
   * user ID to switch during rights dropping
56
   */
57
  uid_t uid;
58
59
  /**
60
   * group ID to switch during rights dropping
61
   */
62
  gid_t gid;
63
64
  /**
65
   * capabilities to keep
66
   */
67
#ifdef CAPABILITIES_LIBCAP
68
  cap_t caps;
69
#endif /* CAPABILITIES_LIBCAP */
70
#ifdef CAPABILITIES_NATIVE
71
  struct __user_cap_data_struct caps[2];
72
#endif /* CAPABILITIES_NATIVE */
73
74
#ifdef EMULATE_R_FUNCS
75
  /**
76
   * mutex to emulate get(pw|gr)nam_r functions
77
   */
78
  mutex_t *mutex;
79
#endif
80
};
81
82
#ifndef WIN32
83
84
/**
85
 * Returns TRUE if the current process/user is member of the given group
86
 */
87
static bool has_group(gid_t group)
88
0
{
89
0
  gid_t *groups;
90
0
  long ngroups, i;
91
0
  bool found = FALSE;
92
93
0
  if (group == getegid())
94
0
  { /* it's unspecified if this is part of the list below or not */
95
0
    return TRUE;
96
0
  }
97
0
  ngroups = sysconf(_SC_NGROUPS_MAX);
98
0
  if (ngroups == -1)
99
0
  {
100
0
    DBG1(DBG_LIB, "getting groups for current process failed: %s",
101
0
       strerror(errno));
102
0
    return FALSE;
103
0
  }
104
0
  groups = calloc(ngroups + 1, sizeof(gid_t));
105
0
  ngroups = getgroups(ngroups, groups);
106
0
  if (ngroups == -1)
107
0
  {
108
0
    DBG1(DBG_LIB, "getting groups for current process failed: %s",
109
0
       strerror(errno));
110
0
    free(groups);
111
0
    return FALSE;
112
0
  }
113
0
  for (i = 0; i < ngroups; i++)
114
0
  {
115
0
    if (group == groups[i])
116
0
    {
117
0
      found = TRUE;
118
0
      break;
119
0
    }
120
0
  }
121
0
  free(groups);
122
0
  return found;
123
0
}
124
125
/**
126
 * Verify that the current process has the given capability
127
 */
128
static bool has_capability(private_capabilities_t *this, u_int cap,
129
               bool *ignore)
130
0
{
131
0
  if (cap == CAP_CHOWN)
132
0
  { /* if new files/UNIX sockets are created they should be owned by the
133
     * configured user and group.  This requires a call to chown(2).  But
134
     * CAP_CHOWN is not always required. */
135
0
    if (!this->uid || geteuid() == this->uid)
136
0
    { /* if the owner does not change CAP_CHOWN is not needed */
137
0
      if (!this->gid || has_group(this->gid))
138
0
      { /* the same applies if the owner is a member of the group */
139
0
        if (ignore)
140
0
        { /* we don't have to keep this, if requested */
141
0
          *ignore = TRUE;
142
0
        }
143
0
        return TRUE;
144
0
      }
145
0
    }
146
0
  }
147
0
#ifndef CAPABILITIES
148
  /* if we can't check the actual capabilities assume only root has it */
149
0
  return geteuid() == 0;
150
0
#endif /* !CAPABILITIES */
151
#ifdef CAPABILITIES_LIBCAP
152
  cap_flag_value_t val;
153
  cap_t caps;
154
  bool ok;
155
156
  caps = cap_get_proc();
157
  if (!caps)
158
  {
159
    return FALSE;
160
  }
161
  ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET;
162
  cap_free(caps);
163
  return ok;
164
#endif /* CAPABILITIES_LIBCAP */
165
#ifdef CAPABILITIES_NATIVE
166
  struct __user_cap_header_struct header = {
167
#if defined(_LINUX_CAPABILITY_VERSION_3)
168
    .version = _LINUX_CAPABILITY_VERSION_3,
169
#elif defined(_LINUX_CAPABILITY_VERSION_2)
170
    .version = _LINUX_CAPABILITY_VERSION_2,
171
#elif defined(_LINUX_CAPABILITY_VERSION_1)
172
    .version = _LINUX_CAPABILITY_VERSION_1,
173
#else
174
    .version = _LINUX_CAPABILITY_VERSION,
175
#endif
176
  };
177
  struct __user_cap_data_struct caps[2];
178
  int i = 0;
179
180
  if (cap >= 32)
181
  {
182
    i++;
183
    cap -= 32;
184
  }
185
  return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap);
186
#endif /* CAPABILITIES_NATIVE */
187
0
}
188
189
#else /* WIN32 */
190
191
/**
192
 * Verify that the current process has the given capability, dummy variant
193
 */
194
static bool has_capability(private_capabilities_t *this, u_int cap,
195
               bool *ignore)
196
{
197
  return TRUE;
198
}
199
200
#endif /* WIN32 */
201
202
/**
203
 * Keep the given capability if it is held by the current process.  Returns
204
 * FALSE, if this is not the case.
205
 */
206
static bool keep_capability(private_capabilities_t *this, u_int cap)
207
0
{
208
#ifdef CAPABILITIES_LIBCAP
209
  cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
210
  cap_set_flag(this->caps, CAP_INHERITABLE, 1, &cap, CAP_SET);
211
  cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET);
212
#endif /* CAPABILITIES_LIBCAP */
213
#ifdef CAPABILITIES_NATIVE
214
  int i = 0;
215
216
  if (cap >= 32)
217
  {
218
    i++;
219
    cap -= 32;
220
  }
221
  this->caps[i].effective |= 1 << cap;
222
  this->caps[i].permitted |= 1 << cap;
223
  this->caps[i].inheritable |= 1 << cap;
224
#endif /* CAPABILITIES_NATIVE */
225
0
  return TRUE;
226
0
}
227
228
METHOD(capabilities_t, keep, bool,
229
  private_capabilities_t *this, u_int cap)
230
0
{
231
0
  bool ignore = FALSE;
232
233
0
  if (!has_capability(this, cap, &ignore))
234
0
  {
235
0
    return FALSE;
236
0
  }
237
0
  else if (ignore)
238
0
  { /* don't keep capabilities that are not required */
239
0
    return TRUE;
240
0
  }
241
0
  return keep_capability(this, cap);
242
0
}
243
244
METHOD(capabilities_t, check, bool,
245
  private_capabilities_t *this, u_int cap)
246
0
{
247
0
  return has_capability(this, cap, NULL);
248
0
}
249
250
METHOD(capabilities_t, get_uid, uid_t,
251
  private_capabilities_t *this)
252
0
{
253
#ifdef WIN32
254
  return this->uid;
255
#else
256
0
  return this->uid ?: geteuid();
257
0
#endif
258
0
}
259
260
METHOD(capabilities_t, get_gid, gid_t,
261
  private_capabilities_t *this)
262
0
{
263
#ifdef WIN32
264
  return this->gid;
265
#else
266
0
  return this->gid ?: getegid();
267
0
#endif
268
0
}
269
270
METHOD(capabilities_t, set_uid, void,
271
  private_capabilities_t *this, uid_t uid)
272
0
{
273
0
  this->uid = uid;
274
0
}
275
276
METHOD(capabilities_t, set_gid, void,
277
  private_capabilities_t *this, gid_t gid)
278
0
{
279
0
  this->gid = gid;
280
0
}
281
282
METHOD(capabilities_t, resolve_uid, bool,
283
  private_capabilities_t *this, char *username)
284
0
{
285
0
#ifndef WIN32
286
0
  struct passwd *pwp;
287
0
  int err;
288
289
0
#ifdef HAVE_GETPWNAM_R
290
0
  struct passwd passwd;
291
0
  size_t buflen = 1024;
292
0
  char *buf = NULL;
293
294
0
  while (TRUE)
295
0
  {
296
0
    buf = realloc(buf, buflen);
297
0
    err = getpwnam_r(username, &passwd, buf, buflen, &pwp);
298
0
    if (err == ERANGE)
299
0
    {
300
0
      buflen *= 2;
301
0
      continue;
302
0
    }
303
0
    if (pwp)
304
0
    {
305
0
      this->uid = pwp->pw_uid;
306
0
    }
307
0
    break;
308
0
  }
309
0
  free(buf);
310
#else /* HAVE GETPWNAM_R */
311
  this->mutex->lock(this->mutex);
312
  pwp = getpwnam(username);
313
  if (pwp)
314
  {
315
    this->uid = pwp->pw_uid;
316
  }
317
  err = errno;
318
  this->mutex->unlock(this->mutex);
319
#endif /* HAVE GETPWNAM_R */
320
0
  if (pwp)
321
0
  {
322
0
    return TRUE;
323
0
  }
324
0
  DBG1(DBG_LIB, "resolving user '%s' failed: %s", username,
325
0
     err ? strerror(err) : "user not found");
326
0
#endif /* !WIN32 */
327
0
  return FALSE;
328
0
}
329
330
METHOD(capabilities_t, resolve_gid, bool,
331
  private_capabilities_t *this, char *groupname)
332
0
{
333
0
#ifndef WIN32
334
0
  struct group *grp;
335
0
  int err;
336
337
0
#ifdef HAVE_GETGRNAM_R
338
0
  struct group group;
339
0
  size_t buflen = 1024;
340
0
  char *buf = NULL;
341
342
0
  while (TRUE)
343
0
  {
344
0
    buf = realloc(buf, buflen);
345
0
    err = getgrnam_r(groupname, &group, buf, buflen, &grp);
346
0
    if (err == ERANGE)
347
0
    {
348
0
      buflen *= 2;
349
0
      continue;
350
0
    }
351
0
    if (grp)
352
0
    {
353
0
      this->gid = grp->gr_gid;
354
0
    }
355
0
    break;
356
0
  }
357
0
  free(buf);
358
#else /* HAVE_GETGRNAM_R */
359
  this->mutex->lock(this->mutex);
360
  grp = getgrnam(groupname);
361
  if (grp)
362
  {
363
    this->gid = grp->gr_gid;
364
  }
365
  err = errno;
366
  this->mutex->unlock(this->mutex);
367
#endif /* HAVE_GETGRNAM_R */
368
0
  if (grp)
369
0
  {
370
0
    return TRUE;
371
0
  }
372
0
  DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname,
373
0
     err ? strerror(err) : "group not found");
374
0
#endif /* !WIN32 */
375
0
  return FALSE;
376
0
}
377
378
#ifndef WIN32
379
/**
380
 * Initialize supplementary groups for unprivileged user
381
 */
382
static bool init_supplementary_groups(private_capabilities_t *this)
383
0
{
384
0
  struct passwd *pwp;
385
0
  int res = -1;
386
387
0
#ifdef HAVE_GETPWUID_R
388
0
  struct passwd pwd;
389
0
  size_t buflen = 1024;
390
0
  char *buf = NULL;
391
392
0
  while (TRUE)
393
0
  {
394
0
    buf = realloc(buf, buflen);
395
0
    if (getpwuid_r(this->uid, &pwd, buf, buflen, &pwp) == ERANGE)
396
0
    {
397
0
      buflen *= 2;
398
0
      continue;
399
0
    }
400
0
    if (pwp)
401
0
    {
402
0
      res = initgroups(pwp->pw_name, this->gid);
403
0
    }
404
0
    break;
405
0
  }
406
0
  free(buf);
407
#else /* HAVE_GETPWUID_R */
408
  this->mutex->lock(this->mutex);
409
  pwp = getpwuid(this->uid);
410
  if (pwp)
411
  {
412
    res = initgroups(pwp->pw_name, this->gid);
413
  }
414
  this->mutex->unlock(this->mutex);
415
#endif /* HAVE_GETPWUID_R */
416
0
  return res == 0;
417
0
}
418
#endif /* WIN32 */
419
420
METHOD(capabilities_t, drop, bool,
421
  private_capabilities_t *this)
422
0
{
423
0
#ifndef WIN32
424
0
#ifdef HAVE_PRCTL
425
0
  if (has_capability(this, CAP_SETPCAP, NULL))
426
0
  {
427
0
    prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
428
0
  }
429
0
#endif
430
431
0
  if (this->uid && !init_supplementary_groups(this))
432
0
  {
433
0
    DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
434
0
       this->uid);
435
0
    return FALSE;
436
0
  }
437
0
  if (this->gid && setgid(this->gid) != 0)
438
0
  {
439
0
    DBG1(DBG_LIB, "change to unprivileged group %u failed: %s",
440
0
       this->gid, strerror(errno));
441
0
    return FALSE;
442
0
  }
443
0
  if (this->uid && setuid(this->uid) != 0)
444
0
  {
445
0
    DBG1(DBG_LIB, "change to unprivileged user %u failed: %s",
446
0
       this->uid, strerror(errno));
447
0
    return FALSE;
448
0
  }
449
450
#ifdef CAPABILITIES_LIBCAP
451
  if (cap_set_proc(this->caps) != 0)
452
  {
453
    DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
454
    return FALSE;
455
  }
456
#endif /* CAPABILITIES_LIBCAP */
457
#ifdef CAPABILITIES_NATIVE
458
  struct __user_cap_header_struct header = {
459
#if defined(_LINUX_CAPABILITY_VERSION_3)
460
    .version = _LINUX_CAPABILITY_VERSION_3,
461
#elif defined(_LINUX_CAPABILITY_VERSION_2)
462
    .version = _LINUX_CAPABILITY_VERSION_2,
463
#elif defined(_LINUX_CAPABILITY_VERSION_1)
464
    .version = _LINUX_CAPABILITY_VERSION_1,
465
#else
466
    .version = _LINUX_CAPABILITY_VERSION,
467
#endif
468
  };
469
  if (capset(&header, this->caps) != 0)
470
  {
471
    DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
472
    return FALSE;
473
  }
474
#endif /* CAPABILITIES_NATIVE */
475
#ifdef CAPABILITIES
476
  DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
477
     geteuid(), getegid());
478
#endif /* CAPABILITIES */
479
0
#endif /*!WIN32 */
480
0
  return TRUE;
481
0
}
482
483
METHOD(capabilities_t, destroy, void,
484
  private_capabilities_t *this)
485
3.51k
{
486
#ifdef EMULATE_R_FUNCS
487
  this->mutex->destroy(this->mutex);
488
#endif /* EMULATE_R_FUNCS */
489
#ifdef CAPABILITIES_LIBCAP
490
  cap_free(this->caps);
491
#endif /* CAPABILITIES_LIBCAP */
492
3.51k
  free(this);
493
3.51k
}
494
495
/**
496
 * See header
497
 */
498
capabilities_t *capabilities_create()
499
3.51k
{
500
3.51k
  private_capabilities_t *this;
501
502
3.51k
  INIT(this,
503
3.51k
    .public = {
504
3.51k
      .keep = _keep,
505
3.51k
      .check = _check,
506
3.51k
      .get_uid = _get_uid,
507
3.51k
      .get_gid = _get_gid,
508
3.51k
      .set_uid = _set_uid,
509
3.51k
      .set_gid = _set_gid,
510
3.51k
      .resolve_uid = _resolve_uid,
511
3.51k
      .resolve_gid = _resolve_gid,
512
3.51k
      .drop = _drop,
513
3.51k
      .destroy = _destroy,
514
3.51k
    },
515
3.51k
  );
516
517
#ifdef CAPABILITIES_LIBCAP
518
  this->caps = cap_init();
519
#endif /* CAPABILITIES_LIBCAP */
520
521
#ifdef EMULATE_R_FUNCS
522
  this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
523
#endif /* EMULATE_R_FUNCS */
524
525
3.51k
  return &this->public;
526
3.51k
}