Line data Source code
1 : /*
2 : SSSD
3 :
4 : Kerberos 5 Backend Module -- Utilities
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "util/util.h"
26 : #include <grp.h>
27 :
28 5 : errno_t become_user(uid_t uid, gid_t gid)
29 : {
30 : uid_t cuid;
31 : int ret;
32 :
33 5 : DEBUG(SSSDBG_FUNC_DATA,
34 : "Trying to become user [%"SPRIuid"][%"SPRIgid"].\n", uid, gid);
35 :
36 : /* skip call if we already are the requested user */
37 5 : cuid = geteuid();
38 5 : if (uid == cuid) {
39 3 : DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
40 3 : return EOK;
41 : }
42 :
43 : /* drop supplmentary groups first */
44 2 : ret = setgroups(0, NULL);
45 2 : if (ret == -1) {
46 0 : ret = errno;
47 0 : DEBUG(SSSDBG_CRIT_FAILURE,
48 : "setgroups failed [%d][%s].\n", ret, strerror(ret));
49 0 : return ret;
50 : }
51 :
52 : /* change gid so that root cannot be regained (changes saved gid too) */
53 2 : ret = setresgid(gid, gid, gid);
54 2 : if (ret == -1) {
55 0 : ret = errno;
56 0 : DEBUG(SSSDBG_CRIT_FAILURE,
57 : "setresgid failed [%d][%s].\n", ret, strerror(ret));
58 0 : return ret;
59 : }
60 :
61 : /* change uid so that root cannot be regained (changes saved uid too) */
62 : /* this call also takes care of dropping CAP_SETUID, so this is a PNR */
63 2 : ret = setresuid(uid, uid, uid);
64 2 : if (ret == -1) {
65 0 : ret = errno;
66 0 : DEBUG(SSSDBG_CRIT_FAILURE,
67 : "setresuid failed [%d][%s].\n", ret, strerror(ret));
68 0 : return ret;
69 : }
70 :
71 2 : return EOK;
72 : }
73 :
74 : struct sss_creds {
75 : uid_t uid;
76 : gid_t gid;
77 : int num_gids;
78 : gid_t gids[];
79 : };
80 :
81 : errno_t restore_creds(struct sss_creds *saved_creds);
82 :
83 : /* This is a reversible version of become_user, and returns the saved
84 : * credentials so that creds can be switched back calling restore_creds */
85 3 : errno_t switch_creds(TALLOC_CTX *mem_ctx,
86 : uid_t uid, gid_t gid,
87 : int num_gids, gid_t *gids,
88 : struct sss_creds **saved_creds)
89 : {
90 3 : struct sss_creds *ssc = NULL;
91 : int size;
92 : int ret;
93 : uid_t myuid;
94 : uid_t mygid;
95 :
96 3 : DEBUG(SSSDBG_FUNC_DATA, "Switch user to [%d][%d].\n", uid, gid);
97 :
98 3 : myuid = geteuid();
99 3 : mygid = getegid();
100 :
101 3 : if (saved_creds) {
102 : /* save current user credentials */
103 2 : size = getgroups(0, NULL);
104 2 : if (size == -1) {
105 0 : ret = errno;
106 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
107 : ret, strerror(ret));
108 0 : goto done;
109 : }
110 :
111 2 : ssc = talloc_size(mem_ctx,
112 : (sizeof(struct sss_creds) + size * sizeof(gid_t)));
113 2 : if (!ssc) {
114 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
115 0 : ret = ENOMEM;
116 0 : goto done;
117 : }
118 2 : ssc->num_gids = size;
119 :
120 2 : size = getgroups(ssc->num_gids, ssc->gids);
121 2 : if (size == -1) {
122 0 : ret = errno;
123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
124 : ret, strerror(ret));
125 : /* free ssc immediately otherwise the code will try to restore
126 : * wrong creds */
127 0 : talloc_zfree(ssc);
128 0 : goto done;
129 : }
130 :
131 : /* we care only about effective ids */
132 2 : ssc->uid = myuid;
133 2 : ssc->gid = mygid;
134 : }
135 :
136 : /* if we are regaining root set euid first so that we have CAP_SETUID back,
137 : * ane the other calls work too, otherwise call it last so that we can
138 : * change groups before we loose CAP_SETUID */
139 3 : if (uid == 0) {
140 1 : ret = setresuid(0, 0, 0);
141 1 : if (ret == -1) {
142 0 : ret = errno;
143 0 : DEBUG(SSSDBG_CRIT_FAILURE,
144 : "setresuid failed [%d][%s].\n", ret, strerror(ret));
145 0 : goto done;
146 : }
147 : }
148 :
149 : /* TODO: use libcap-ng if we need to get/set capabilities too ? */
150 :
151 3 : if (myuid == uid && mygid == gid) {
152 1 : DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
153 1 : return EOK;
154 : }
155 :
156 : /* try to setgroups first should always work if CAP_SETUID is set,
157 : * otherwise it will always fail, failure is not critical though as
158 : * generally we only really care about uid and at mot primary gid */
159 2 : ret = setgroups(num_gids, gids);
160 2 : if (ret == -1) {
161 0 : ret = errno;
162 0 : DEBUG(SSSDBG_TRACE_FUNC,
163 : "setgroups failed [%d][%s].\n", ret, strerror(ret));
164 : }
165 :
166 : /* change gid now, (leaves saved gid to current, so we can restore) */
167 2 : ret = setresgid(-1, gid, -1);
168 2 : if (ret == -1) {
169 0 : ret = errno;
170 0 : DEBUG(SSSDBG_CRIT_FAILURE,
171 : "setresgid failed [%d][%s].\n", ret, strerror(ret));
172 0 : goto done;
173 : }
174 :
175 2 : if (uid != 0) {
176 : /* change uid, (leaves saved uid to current, so we can restore) */
177 1 : ret = setresuid(-1, uid, -1);
178 1 : if (ret == -1) {
179 0 : ret = errno;
180 0 : DEBUG(SSSDBG_CRIT_FAILURE,
181 : "setresuid failed [%d][%s].\n", ret, strerror(ret));
182 0 : goto done;
183 : }
184 : }
185 :
186 2 : ret = 0;
187 :
188 : done:
189 2 : if (ret) {
190 : /* attempt to restore creds first */
191 0 : restore_creds(ssc);
192 0 : talloc_free(ssc);
193 2 : } else if (saved_creds) {
194 1 : *saved_creds = ssc;
195 : }
196 2 : return ret;
197 : }
198 :
199 1 : errno_t restore_creds(struct sss_creds *saved_creds)
200 : {
201 1 : if (saved_creds == NULL) {
202 : /* In case save_creds was saved with the UID already dropped */
203 0 : return EOK;
204 : }
205 :
206 1 : return switch_creds(saved_creds,
207 : saved_creds->uid,
208 : saved_creds->gid,
209 : saved_creds->num_gids,
210 1 : saved_creds->gids, NULL);
211 : }
|