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 0 : errno_t become_user(uid_t uid, gid_t gid)
29 : {
30 : uid_t cuid;
31 : int ret;
32 :
33 0 : 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 0 : cuid = geteuid();
38 0 : if (uid == cuid) {
39 0 : DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
40 0 : return EOK;
41 : }
42 :
43 : /* drop supplmentary groups first */
44 0 : ret = setgroups(0, NULL);
45 0 : 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 0 : ret = setresgid(gid, gid, gid);
54 0 : 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 0 : ret = setresuid(uid, uid, uid);
64 0 : 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 0 : 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 0 : 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 0 : struct sss_creds *ssc = NULL;
91 : int size;
92 : int ret;
93 : uid_t myuid;
94 : uid_t mygid;
95 :
96 0 : DEBUG(SSSDBG_FUNC_DATA, "Switch user to [%d][%d].\n", uid, gid);
97 :
98 0 : myuid = geteuid();
99 0 : mygid = getegid();
100 :
101 0 : if (saved_creds) {
102 : /* save current user credentials */
103 0 : size = getgroups(0, NULL);
104 0 : 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 0 : ssc = talloc_size(mem_ctx,
112 : (sizeof(struct sss_creds) + size * sizeof(gid_t)));
113 0 : if (!ssc) {
114 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
115 0 : ret = ENOMEM;
116 0 : goto done;
117 : }
118 0 : ssc->num_gids = size;
119 :
120 0 : size = getgroups(ssc->num_gids, ssc->gids);
121 0 : 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 0 : ssc->uid = myuid;
133 0 : 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 0 : if (uid == 0) {
140 0 : ret = setresuid(0, 0, 0);
141 0 : 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 0 : if (myuid == uid && mygid == gid) {
152 0 : DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
153 0 : talloc_zfree(ssc);
154 0 : return EOK;
155 : }
156 :
157 : /* try to setgroups first should always work if CAP_SETUID is set,
158 : * otherwise it will always fail, failure is not critical though as
159 : * generally we only really care about uid and at mot primary gid */
160 0 : ret = setgroups(num_gids, gids);
161 0 : if (ret == -1) {
162 0 : ret = errno;
163 0 : DEBUG(SSSDBG_TRACE_FUNC,
164 : "setgroups failed [%d][%s].\n", ret, strerror(ret));
165 : }
166 :
167 : /* change gid now, (leaves saved gid to current, so we can restore) */
168 0 : ret = setresgid(-1, gid, -1);
169 0 : if (ret == -1) {
170 0 : ret = errno;
171 0 : DEBUG(SSSDBG_CRIT_FAILURE,
172 : "setresgid failed [%d][%s].\n", ret, strerror(ret));
173 0 : goto done;
174 : }
175 :
176 0 : if (uid != 0) {
177 : /* change uid, (leaves saved uid to current, so we can restore) */
178 0 : ret = setresuid(-1, uid, -1);
179 0 : if (ret == -1) {
180 0 : ret = errno;
181 0 : DEBUG(SSSDBG_CRIT_FAILURE,
182 : "setresuid failed [%d][%s].\n", ret, strerror(ret));
183 0 : goto done;
184 : }
185 : }
186 :
187 0 : ret = 0;
188 :
189 : done:
190 0 : if (ret) {
191 : /* attempt to restore creds first */
192 0 : restore_creds(ssc);
193 0 : talloc_free(ssc);
194 0 : } else if (saved_creds) {
195 0 : *saved_creds = ssc;
196 : }
197 0 : return ret;
198 : }
199 :
200 0 : errno_t restore_creds(struct sss_creds *saved_creds)
201 : {
202 0 : if (saved_creds == NULL) {
203 : /* In case save_creds was saved with the UID already dropped */
204 0 : return EOK;
205 : }
206 :
207 0 : return switch_creds(saved_creds,
208 : saved_creds->uid,
209 : saved_creds->gid,
210 : saved_creds->num_gids,
211 0 : saved_creds->gids, NULL);
212 : }
|