~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002101901

« back to all changes in this revision

Viewing changes to src/lib/restrict-access.c

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2008-05-26 09:51:01 UTC
  • mfrom: (1.10.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20080526095101-y0zxqc8ofd9j1aa5
Tags: 1:1.0.13-4ubuntu1
* Merge from debian unstable, remaining changes:
  - DebainMaintainerField
  - Use Snakeoil SSL certificate by default.
    + debian/control: Depend on ssl-cert
    + debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert paths
      to snakeoil.
    + debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
  - Fast TearDown:
    + debian/rules: Call dh_installinit in 'multiuser' mode.
    + debian/control: Depend on newer sysv-rc for this.
    + debian/dovecot-common.postinst: Remove stp script symlinks from rc0 and rc6 on upgrades.
      Need to be kept unil next LTS release.
  - Add autopkgtest in debian/tests/*.
  - Don't fail in postinst if dovecot-{sql,ldap} is missing. (LP: #153161)
  - Dropped upstream-mail-group-fixes.dpatch. No longer needed.
  - Dropped upstream-invalid-password-fixes.dpatch. No longer needed.
  - debian/dovecot-common.init: Check to see if there is an /etc/inetd.conf. (LP: #208411)
  - debian/patches/login-max-processes-count-warning.dpatch: Tell the user
    that they have reached the maxium number of processes count. (LP: #189616)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2002-2004 Timo Sirainen */
 
1
/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#define _GNU_SOURCE /* setresgid() */
 
4
#include <sys/types.h>
 
5
#include <unistd.h>
2
6
 
3
7
#include "lib.h"
4
8
#include "restrict-access.h"
5
9
#include "env-util.h"
6
10
 
7
11
#include <stdlib.h>
8
 
#include <unistd.h>
9
12
#include <time.h>
10
13
#include <grp.h>
11
14
 
12
 
void restrict_access_set_env(const char *user, uid_t uid, gid_t gid,
 
15
static gid_t primary_gid = (gid_t)-1, privileged_gid = (gid_t)-1;
 
16
static bool using_priv_gid = FALSE;
 
17
 
 
18
void restrict_access_set_env(const char *user, uid_t uid,
 
19
                             gid_t gid, gid_t privileged_gid,
13
20
                             const char *chroot_dir,
14
21
                             gid_t first_valid_gid, gid_t last_valid_gid,
15
22
                             const char *extra_groups)
21
28
 
22
29
        env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid)));
23
30
        env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid)));
 
31
        if (privileged_gid != (gid_t)-1) {
 
32
                env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s",
 
33
                                        dec2str(privileged_gid)));
 
34
        }
24
35
        if (extra_groups != NULL && *extra_groups != '\0') {
25
36
                env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
26
37
                                    extra_groups, NULL));
36
47
        }
37
48
}
38
49
 
39
 
static gid_t *get_groups_list(int *gid_count_r)
 
50
static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid)
 
51
{
 
52
        if (privileged_gid == (gid_t)-1) {
 
53
                if (primary_gid == getgid() && primary_gid == getegid()) {
 
54
                        /* everything is already set */
 
55
                        return;
 
56
                }
 
57
 
 
58
                if (setgid(primary_gid) != 0) {
 
59
                        i_fatal("setgid(%s) failed with euid=%s, "
 
60
                                "gid=%s, egid=%s: %m",
 
61
                                dec2str(primary_gid), dec2str(geteuid()),
 
62
                                dec2str(getgid()), dec2str(getegid()));
 
63
                }
 
64
                return;
 
65
        }
 
66
 
 
67
        if (getegid() != 0 && primary_gid == getgid() &&
 
68
            primary_gid == getegid()) {
 
69
                /* privileged_gid is hopefully in saved ID. if not,
 
70
                   there's nothing we can do about it. */
 
71
                return;
 
72
        }
 
73
 
 
74
#ifdef HAVE_SETRESGID
 
75
        if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) {
 
76
                i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
 
77
                        dec2str(primary_gid), dec2str(primary_gid),
 
78
                        dec2str(privileged_gid), dec2str(geteuid()));
 
79
        }
 
80
#else
 
81
        if (geteuid() == 0) {
 
82
                /* real, effective, saved -> privileged_gid */
 
83
                if (setgid(privileged_gid) < 0) {
 
84
                        i_fatal("setgid(%s) failed: %m",
 
85
                                dec2str(privileged_gid));
 
86
                }
 
87
        }
 
88
        /* real, effective -> primary_gid
 
89
           saved -> keep */
 
90
        if (setregid(primary_gid, primary_gid) != 0) {
 
91
                i_fatal("setregid(%s,%s) failed with euid=%s: %m",
 
92
                        dec2str(primary_gid), dec2str(privileged_gid),
 
93
                        dec2str(geteuid()));
 
94
        }
 
95
#endif
 
96
}
 
97
 
 
98
static gid_t *get_groups_list(unsigned int *gid_count_r)
40
99
{
41
100
        gid_t *gid_list;
42
101
        int ret, gid_count;
53
112
        return gid_list;
54
113
}
55
114
 
56
 
static void drop_restricted_groups(bool *have_root_group)
 
115
static void drop_restricted_groups(gid_t *gid_list, unsigned int *gid_count,
 
116
                                   bool *have_root_group)
57
117
{
58
118
        /* @UNSAFE */
 
119
        gid_t first_valid, last_valid;
59
120
        const char *env;
60
 
        gid_t *gid_list, first_valid_gid, last_valid_gid;
61
 
        int i, used, gid_count;
 
121
        unsigned int i, used;
62
122
 
63
123
        env = getenv("RESTRICT_GID_FIRST");
64
 
        first_valid_gid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
 
124
        first_valid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
65
125
        env = getenv("RESTRICT_GID_LAST");
66
 
        last_valid_gid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
67
 
 
68
 
        if (first_valid_gid == 0 && last_valid_gid == 0)
69
 
                return;
70
 
 
71
 
        t_push();
72
 
        gid_list = get_groups_list(&gid_count);
73
 
 
74
 
        for (i = 0, used = 0; i < gid_count; i++) {
75
 
                if (gid_list[i] >= first_valid_gid &&
76
 
                    (last_valid_gid == 0 || gid_list[i] <= last_valid_gid)) {
 
126
        last_valid = env == NULL ? (gid_t)-1 : (gid_t)strtoul(env, NULL, 10);
 
127
 
 
128
        for (i = 0, used = 0; i < *gid_count; i++) {
 
129
                if (gid_list[i] >= first_valid &&
 
130
                    (last_valid == (gid_t)-1 || gid_list[i] <= last_valid)) {
77
131
                        if (gid_list[i] == 0)
78
132
                                *have_root_group = TRUE;
79
133
                        gid_list[used++] = gid_list[i];
80
134
                }
81
135
        }
82
 
 
83
 
        if (used != gid_count) {
84
 
                /* it did contain restricted groups, remove it */
85
 
                if (setgroups(used, gid_list) < 0)
86
 
                        i_fatal("setgroups() failed: %m");
87
 
        }
88
 
        t_pop();
 
136
        *gid_count = used;
89
137
}
90
138
 
91
139
static gid_t get_group_id(const char *name)
101
149
        return group->gr_gid;
102
150
}
103
151
 
104
 
static void grant_extra_groups(const char *groups)
 
152
static void fix_groups_list(const char *extra_groups,
 
153
                            bool preserve_existing, bool *have_root_group)
105
154
{
106
 
        const char *const *tmp;
107
 
        gid_t *gid_list;
108
 
        int gid_count;
109
 
 
110
 
        t_push();
111
 
        tmp = t_strsplit(groups, ", ");
112
 
        gid_list = get_groups_list(&gid_count);
113
 
        for (; *tmp != NULL; tmp++) {
114
 
                if (**tmp == '\0')
115
 
                        continue;
116
 
 
117
 
                if (!t_try_realloc(gid_list, (gid_count+1) * sizeof(gid_t)))
118
 
                        i_unreached();
119
 
                gid_list[gid_count++] = get_group_id(*tmp);
120
 
        }
121
 
 
122
 
        if (setgroups(gid_count, gid_list) < 0)
123
 
                i_fatal("setgroups() failed: %m");
124
 
 
125
 
        t_pop();
 
155
        gid_t gid, *gid_list, *gid_list2;
 
156
        const char *const *tmp, *empty = NULL;
 
157
        unsigned int i, gid_count;
 
158
        bool add_primary_gid;
 
159
 
 
160
        /* if we're using a privileged GID, we can temporarily drop our
 
161
           effective GID. we still want to be able to use its privileges,
 
162
           so add it to supplementary groups. */
 
163
        add_primary_gid = privileged_gid != (gid_t)-1;
 
164
 
 
165
        tmp = extra_groups == NULL ? &empty :
 
166
                t_strsplit_spaces(extra_groups, ", ");
 
167
 
 
168
        if (preserve_existing) {
 
169
                gid_list = get_groups_list(&gid_count);
 
170
                drop_restricted_groups(gid_list, &gid_count,
 
171
                                       have_root_group);
 
172
                /* see if the list already contains the primary GID */
 
173
                for (i = 0; i < gid_count; i++) {
 
174
                        if (gid_list[i] == primary_gid) {
 
175
                                add_primary_gid = FALSE;
 
176
                                break;
 
177
                        }
 
178
                }
 
179
        } else {
 
180
                gid_list = NULL;
 
181
                gid_count = 0;
 
182
        }
 
183
        if (gid_count == 0) {
 
184
                /* Some OSes don't like an empty groups list,
 
185
                   so use the primary GID as the only one. */
 
186
                gid_list = t_new(gid_t, 2);
 
187
                gid_list[0] = primary_gid;
 
188
                gid_count = 1;
 
189
                add_primary_gid = FALSE;
 
190
        }
 
191
 
 
192
        if (*tmp != NULL || add_primary_gid) {
 
193
                /* @UNSAFE: add extra groups and/or primary GID to gids list */
 
194
                gid_list2 = t_new(gid_t, gid_count + strarray_length(tmp) + 1);
 
195
                memcpy(gid_list2, gid_list, gid_count * sizeof(gid_t));
 
196
                for (; *tmp != NULL; tmp++) {
 
197
                        gid = get_group_id(*tmp);
 
198
                        if (gid != primary_gid)
 
199
                                gid_list2[gid_count++] = gid;
 
200
                }
 
201
                if (add_primary_gid)
 
202
                        gid_list2[gid_count++] = primary_gid;
 
203
                gid_list = gid_list2;
 
204
        }
 
205
 
 
206
        if (setgroups(gid_count, gid_list) < 0) {
 
207
                if (errno == EINVAL) {
 
208
                        i_fatal("setgroups(%s) failed: Too many extra groups",
 
209
                                extra_groups == NULL ? "" : extra_groups);
 
210
                } else {
 
211
                        i_fatal("setgroups() failed: %m");
 
212
                }
 
213
        }
126
214
}
127
215
 
128
216
void restrict_access_by_env(bool disallow_root)
129
217
{
130
218
        const char *env;
131
 
        gid_t gid;
132
219
        uid_t uid;
133
 
        bool have_root_group;
134
 
 
135
 
        /* groups - the getgid() checks are just so we don't fail if we're
136
 
           not running as root and try to just use our own GID. Do this
137
 
           before chrooting so initgroups() actually works. */
 
220
        bool is_root, have_root_group, preserve_groups = FALSE;
 
221
        bool allow_root_gid;
 
222
 
 
223
        is_root = geteuid() == 0;
 
224
 
 
225
        /* set the primary/privileged group */
138
226
        env = getenv("RESTRICT_SETGID");
139
 
        gid = env == NULL ? 0 : (gid_t)strtoul(env, NULL, 10);
140
 
        have_root_group = gid == 0;
141
 
        if (gid != 0 && (gid != getgid() || gid != getegid())) {
142
 
                if (setgid(gid) != 0) {
143
 
                        i_fatal("setgid(%s) failed with euid=%s, egid=%s: %m",
144
 
                                dec2str(gid), dec2str(geteuid()),
145
 
                                dec2str(getegid()));
146
 
                }
147
 
 
148
 
                env = getenv("RESTRICT_USER");
149
 
                if (env == NULL) {
150
 
                        /* user not known, use only this one group */
151
 
                        if (setgroups(1, &gid) < 0) {
152
 
                                i_fatal("setgroups(%s) failed: %m",
153
 
                                        dec2str(gid));
154
 
                        }
155
 
                } else {
156
 
                        if (initgroups(env, gid) != 0) {
157
 
                                i_fatal("initgroups(%s, %s) failed: %m",
158
 
                                        env, dec2str(gid));
159
 
                        }
160
 
 
161
 
                        drop_restricted_groups(&have_root_group);
162
 
                }
163
 
        }
164
 
 
165
 
        /* grant additional groups to process */
 
227
        primary_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
 
228
                (gid_t)strtoul(env, NULL, 10);
 
229
        env = getenv("RESTRICT_SETGID_PRIV");
 
230
        privileged_gid = env == NULL || *env == '\0' ? (gid_t)-1 :
 
231
                (gid_t)strtoul(env, NULL, 10);
 
232
 
 
233
        have_root_group = primary_gid == 0;
 
234
        if (primary_gid != (gid_t)-1 || privileged_gid != (gid_t)-1) {
 
235
                if (primary_gid == (gid_t)-1)
 
236
                        primary_gid = getegid();
 
237
                restrict_init_groups(primary_gid, privileged_gid);
 
238
        } else {
 
239
                if (primary_gid == (gid_t)-1)
 
240
                        primary_gid = getegid();
 
241
        }
 
242
 
 
243
        /* set system user's groups */
 
244
        env = getenv("RESTRICT_USER");
 
245
        if (env != NULL && *env != '\0' && is_root) {
 
246
                if (initgroups(env, primary_gid) < 0) {
 
247
                        i_fatal("initgroups(%s, %s) failed: %m",
 
248
                                env, dec2str(primary_gid));
 
249
                }
 
250
                preserve_groups = TRUE;
 
251
        }
 
252
 
 
253
        /* add extra groups. if we set system user's groups, drop the
 
254
           restricted groups at the same time. */
166
255
        env = getenv("RESTRICT_SETEXTRAGROUPS");
167
 
        if (env != NULL && *env != '\0')
168
 
                grant_extra_groups(env);
 
256
        if (is_root) {
 
257
                t_push();
 
258
                fix_groups_list(env, preserve_groups, &have_root_group);
 
259
                t_pop();
 
260
        }
169
261
 
170
262
        /* chrooting */
171
263
        env = getenv("RESTRICT_CHROOT");
193
285
 
194
286
        /* uid last */
195
287
        env = getenv("RESTRICT_SETUID");
196
 
        uid = env == NULL ? 0 : (uid_t)strtoul(env, NULL, 10);
 
288
        uid = env == NULL || *env == '\0' ? 0 : (uid_t)strtoul(env, NULL, 10);
197
289
        if (uid != 0) {
198
290
                if (setuid(uid) != 0) {
199
291
                        i_fatal("setuid(%s) failed with euid=%s: %m",
211
303
        }
212
304
 
213
305
        env = getenv("RESTRICT_GID_FIRST");
214
 
        if ((!have_root_group || (env != NULL && atoi(env) != 0)) && uid != 0) {
 
306
        if (env != NULL && atoi(env) != 0)
 
307
                allow_root_gid = FALSE;
 
308
        else if (primary_gid == 0 || privileged_gid == 0)
 
309
                allow_root_gid = TRUE;
 
310
        else
 
311
                allow_root_gid = FALSE;
 
312
 
 
313
        if (!allow_root_gid && uid != 0) {
215
314
                if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) {
216
 
                        if (gid == 0)
 
315
                        if (primary_gid == 0)
217
316
                                i_fatal("GID 0 isn't permitted");
218
317
                        i_fatal("We couldn't drop root group privileges "
219
 
                                "(wanted=%s, gid=%s, egid=%s)", dec2str(gid),
 
318
                                "(wanted=%s, gid=%s, egid=%s)",
 
319
                                dec2str(primary_gid),
220
320
                                dec2str(getgid()), dec2str(getegid()));
221
321
                }
222
322
        }
225
325
        env_put("RESTRICT_USER=");
226
326
        env_put("RESTRICT_CHROOT=");
227
327
        env_put("RESTRICT_SETUID=");
228
 
        env_put("RESTRICT_SETGID=");
 
328
        if (privileged_gid == (gid_t)-1) {
 
329
                /* if we're dropping privileges before executing and
 
330
                   a privileged group is set, the groups must be fixed
 
331
                   after exec */
 
332
                env_put("RESTRICT_SETGID=");
 
333
                env_put("RESTRICT_SETGID_PRIV=");
 
334
        }
229
335
        env_put("RESTRICT_SETEXTRAGROUPS=");
230
336
        env_put("RESTRICT_GID_FIRST=");
231
337
        env_put("RESTRICT_GID_LAST=");
232
338
}
 
339
 
 
340
int restrict_access_use_priv_gid(void)
 
341
{
 
342
        i_assert(!using_priv_gid);
 
343
 
 
344
        if (privileged_gid == (gid_t)-1)
 
345
                return 0;
 
346
        if (setegid(privileged_gid) < 0) {
 
347
                i_error("setegid(privileged) failed: %m");
 
348
                return -1;
 
349
        }
 
350
        using_priv_gid = TRUE;
 
351
        return 0;
 
352
}
 
353
 
 
354
void restrict_access_drop_priv_gid(void)
 
355
{
 
356
        if (!using_priv_gid)
 
357
                return;
 
358
 
 
359
        if (setegid(primary_gid) < 0)
 
360
                i_fatal("setegid(primary) failed: %m");
 
361
        using_priv_gid = FALSE;
 
362
}
 
363
 
 
364
bool restrict_access_have_priv_gid(void)
 
365
{
 
366
        return privileged_gid != (gid_t)-1;
 
367
}