Line data Source code
1 : /*
2 : SSSD
3 :
4 : tools_mc_util - interface to the memcache for userspace tools
5 :
6 : Copyright (C) Red Hat 2013
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <talloc.h>
23 : #include <fcntl.h>
24 : #include <sys/stat.h>
25 :
26 : #include "db/sysdb.h"
27 : #include "util/util.h"
28 : #include "tools/tools_util.h"
29 : #include "util/mmap_cache.h"
30 : #include "util/sss_cli_cmd.h"
31 : #include "sss_client/sss_cli.h"
32 :
33 : /* This is a copy of sss_mc_set_recycled present in
34 : * src/responder/nss/nsssrv_mmap_cache.c. If you modify this function,
35 : * you should modify the original function too. */
36 0 : static errno_t sss_mc_set_recycled(int fd)
37 : {
38 0 : uint32_t w = SSS_MC_HEADER_RECYCLED;
39 : struct sss_mc_header h;
40 : off_t offset;
41 : off_t pos;
42 : ssize_t written;
43 :
44 0 : offset = MC_PTR_DIFF(&h.status, &h);
45 :
46 0 : pos = lseek(fd, offset, SEEK_SET);
47 0 : if (pos == -1) {
48 : /* What do we do now ? */
49 0 : return errno;
50 : }
51 :
52 0 : errno = 0;
53 0 : written = sss_atomic_write_s(fd, (uint8_t *)&w, sizeof(h.status));
54 0 : if (written == -1) {
55 0 : return errno;
56 : }
57 :
58 0 : if (written != sizeof(h.status)) {
59 : /* Write error */
60 0 : return EIO;
61 : }
62 :
63 0 : return EOK;
64 : }
65 :
66 0 : errno_t sss_memcache_invalidate(const char *mc_filename)
67 : {
68 0 : int mc_fd = -1;
69 : errno_t ret;
70 : errno_t pret;
71 0 : useconds_t t = 50000;
72 0 : int retries = 2;
73 :
74 0 : if (!mc_filename) {
75 0 : return EINVAL;
76 : }
77 :
78 0 : mc_fd = open(mc_filename, O_RDWR);
79 0 : if (mc_fd == -1) {
80 0 : ret = errno;
81 0 : if (ret == ENOENT) {
82 0 : DEBUG(SSSDBG_TRACE_FUNC,"Memory cache file %s "
83 : "does not exist.\n", mc_filename);
84 0 : return EOK;
85 : } else {
86 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s: %s\n",
87 : mc_filename, strerror(ret));
88 0 : return ret;
89 : }
90 : }
91 :
92 0 : ret = sss_br_lock_file(mc_fd, 0, 1, retries, t);
93 0 : if (ret == EACCES) {
94 0 : DEBUG(SSSDBG_TRACE_FUNC,
95 : "File %s already locked by someone else.\n", mc_filename);
96 0 : goto done;
97 0 : } else if (ret != EOK) {
98 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to lock file %s.\n", mc_filename);
99 0 : goto done;
100 : }
101 : /* Mark the mc file as recycled. */
102 0 : ret = sss_mc_set_recycled(mc_fd);
103 0 : if (ret != EOK) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to mark memory cache file %s "
105 : "as recycled.\n", mc_filename);
106 0 : goto done;
107 : }
108 :
109 0 : ret = EOK;
110 : done:
111 0 : if (mc_fd != -1) {
112 : /* Closing the file also releases the lock */
113 0 : close(mc_fd);
114 :
115 : /* Only unlink the file if invalidation was successful */
116 0 : if (ret == EOK) {
117 0 : pret = unlink(mc_filename);
118 0 : if (pret == -1) {
119 0 : DEBUG(SSSDBG_MINOR_FAILURE,
120 : "Failed to unlink file %s. "
121 : "Will be unlinked later by sssd_nss.\n",
122 : mc_filename);
123 : }
124 : }
125 : }
126 0 : return ret;
127 : }
128 :
129 0 : static int clear_fastcache(bool *sssd_nss_is_off)
130 : {
131 : int ret;
132 0 : ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/passwd");
133 0 : if (ret != EOK) {
134 0 : if (ret == EACCES) {
135 0 : *sssd_nss_is_off = false;
136 0 : return EOK;
137 : } else {
138 0 : return ret;
139 : }
140 : }
141 :
142 0 : ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/group");
143 0 : if (ret != EOK) {
144 0 : if (ret == EACCES) {
145 0 : *sssd_nss_is_off = false;
146 0 : return EOK;
147 : } else {
148 0 : return ret;
149 : }
150 : }
151 :
152 0 : ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/initgroups");
153 0 : if (ret != EOK) {
154 0 : if (ret == EACCES) {
155 0 : *sssd_nss_is_off = false;
156 0 : return EOK;
157 : } else {
158 0 : return ret;
159 : }
160 : }
161 :
162 0 : *sssd_nss_is_off = true;
163 0 : return EOK;
164 : }
165 :
166 0 : static errno_t wait_till_nss_responder_invalidate_cache(void)
167 : {
168 0 : struct stat stat_buf = { 0 };
169 0 : const time_t max_wait = 1000000; /* 1 second */
170 0 : const time_t step_time = 5000; /* 5 miliseconds */
171 0 : const size_t steps_count = max_wait / step_time;
172 : int ret;
173 :
174 0 : for (size_t i = 0; i < steps_count; ++i) {
175 0 : ret = stat(SSS_NSS_MCACHE_DIR "/" CLEAR_MC_FLAG, &stat_buf);
176 0 : if (ret == -1) {
177 0 : ret = errno;
178 0 : if (ret == ENOENT) {
179 : /* nss responder has already invalidated memory caches */
180 0 : return EOK;
181 : }
182 :
183 0 : DEBUG(SSSDBG_CRIT_FAILURE,
184 : "stat failed: %s (%d)\n", sss_strerror(ret), ret);
185 : }
186 :
187 0 : usleep(step_time);
188 : }
189 :
190 0 : return EAGAIN;
191 : }
192 :
193 0 : errno_t sss_memcache_clear_all(void)
194 : {
195 : errno_t ret;
196 0 : bool sssd_nss_is_off = false;
197 : FILE *clear_mc_flag;
198 :
199 0 : ret = clear_fastcache(&sssd_nss_is_off);
200 0 : if (ret != EOK) {
201 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to clear caches.\n");
202 0 : return EIO;
203 : }
204 0 : if (!sssd_nss_is_off) {
205 : /* sssd_nss is running -> signal monitor to invalidate fastcache */
206 0 : clear_mc_flag = fopen(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, "w");
207 0 : if (clear_mc_flag == NULL) {
208 0 : DEBUG(SSSDBG_CRIT_FAILURE,
209 : "Failed to create clear_mc_flag file. "
210 : "Memory cache will not be cleared.\n");
211 0 : return EIO;
212 : }
213 0 : ret = fclose(clear_mc_flag);
214 0 : if (ret != 0) {
215 0 : ret = errno;
216 0 : DEBUG(SSSDBG_CRIT_FAILURE,
217 : "Unable to close file descriptor: %s\n",
218 : strerror(ret));
219 0 : return EIO;
220 : }
221 0 : DEBUG(SSSDBG_TRACE_FUNC, "Sending SIGHUP to monitor.\n");
222 0 : ret = signal_sssd(SIGHUP);
223 0 : if (ret != EOK) {
224 0 : DEBUG(SSSDBG_CRIT_FAILURE,
225 : "Failed to send SIGHUP to monitor.\n");
226 0 : return EIO;
227 : }
228 :
229 0 : ret = wait_till_nss_responder_invalidate_cache();
230 0 : if (ret != EOK) {
231 0 : ERROR("The fast memory caches was not invalidated by NSS "
232 : "responder.\n");
233 : }
234 : }
235 :
236 0 : return EOK;
237 : }
238 :
239 : enum sss_tools_ent {
240 : SSS_TOOLS_USER,
241 : SSS_TOOLS_GROUP
242 : };
243 :
244 0 : static errno_t sss_mc_refresh_ent(const char *name, enum sss_tools_ent ent)
245 : {
246 : enum sss_cli_command cmd;
247 : struct sss_cli_req_data rd;
248 0 : uint8_t *repbuf = NULL;
249 : size_t replen;
250 : enum nss_status nret;
251 : errno_t ret;
252 :
253 0 : cmd = SSS_CLI_NULL;
254 0 : switch (ent) {
255 : case SSS_TOOLS_USER:
256 0 : cmd = SSS_NSS_GETPWNAM;
257 0 : break;
258 : case SSS_TOOLS_GROUP:
259 0 : cmd = SSS_NSS_GETGRNAM;
260 0 : break;
261 : }
262 :
263 0 : if (cmd == SSS_CLI_NULL) {
264 0 : DEBUG(SSSDBG_OP_FAILURE, "Unknown object [%d][%s] to refresh\n",
265 : cmd, sss_cmd2str(cmd));
266 0 : return EINVAL;
267 : }
268 :
269 0 : rd.data = name;
270 0 : rd.len = strlen(name) + 1;
271 :
272 0 : sss_nss_lock();
273 0 : nret = sss_nss_make_request(cmd, &rd, &repbuf, &replen, &ret);
274 0 : sss_nss_unlock();
275 :
276 0 : free(repbuf);
277 0 : if (nret != NSS_STATUS_SUCCESS && nret != NSS_STATUS_NOTFOUND) {
278 0 : return EIO;
279 : }
280 :
281 0 : return EOK;
282 : }
283 :
284 0 : errno_t sss_mc_refresh_user(const char *username)
285 : {
286 0 : return sss_mc_refresh_ent(username, SSS_TOOLS_USER);
287 : }
288 :
289 0 : errno_t sss_mc_refresh_group(const char *groupname)
290 : {
291 0 : return sss_mc_refresh_ent(groupname, SSS_TOOLS_GROUP);
292 : }
293 :
294 0 : errno_t sss_mc_refresh_nested_group(struct tools_ctx *tctx,
295 : const char *name)
296 : {
297 : errno_t ret;
298 : struct ldb_message *msg;
299 : struct ldb_message_element *el;
300 0 : const char *attrs[] = { SYSDB_MEMBEROF,
301 : SYSDB_NAME,
302 : NULL };
303 : size_t i;
304 : char *parent_name;
305 :
306 0 : ret = sss_mc_refresh_group(name);
307 0 : if (ret != EOK) {
308 0 : DEBUG(SSSDBG_MINOR_FAILURE,
309 : "Cannot refresh group %s from memory cache\n", name);
310 : /* try to carry on */
311 : }
312 :
313 0 : ret = sysdb_search_group_by_name(tctx, tctx->local, name, attrs, &msg);
314 0 : if (ret) {
315 0 : DEBUG(SSSDBG_OP_FAILURE,
316 : "Search failed: %s (%d)\n", strerror(ret), ret);
317 0 : return ret;
318 : }
319 :
320 0 : el = ldb_msg_find_element(msg, SYSDB_MEMBEROF);
321 0 : if (!el || el->num_values == 0) {
322 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Group %s has no parents\n", name);
323 0 : talloc_free(msg);
324 0 : return EOK;
325 : }
326 :
327 : /* This group is nested. We need to invalidate all its parents, too */
328 0 : for (i=0; i < el->num_values; i++) {
329 0 : ret = sysdb_group_dn_name(tctx->sysdb, tctx,
330 0 : (const char *) el->values[i].data,
331 : &parent_name);
332 0 : if (ret != EOK) {
333 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Malformed DN [%s]? Skipping\n",
334 : (const char *) el->values[i].data);
335 0 : talloc_free(parent_name);
336 0 : continue;
337 : }
338 :
339 0 : ret = sss_mc_refresh_group(parent_name);
340 0 : talloc_free(parent_name);
341 0 : if (ret != EOK) {
342 0 : DEBUG(SSSDBG_MINOR_FAILURE,
343 : "Cannot refresh group %s from memory cache\n", name);
344 : /* try to carry on */
345 : }
346 : }
347 :
348 0 : talloc_free(msg);
349 0 : return EOK;
350 : }
351 :
352 0 : errno_t sss_mc_refresh_grouplist(struct tools_ctx *tctx,
353 : char **groupnames)
354 : {
355 : int i;
356 : errno_t ret;
357 0 : bool failed = false;
358 :
359 0 : if (!groupnames) return EOK;
360 :
361 0 : for (i = 0; groupnames[i]; i++) {
362 0 : ret = sss_mc_refresh_nested_group(tctx, groupnames[i]);
363 0 : if (ret != EOK) {
364 0 : DEBUG(SSSDBG_MINOR_FAILURE,
365 : "Cannot refresh group %s from memory cache\n",
366 : groupnames[i]);
367 0 : failed = true;
368 0 : continue;
369 : }
370 : }
371 :
372 0 : return failed ? EIO : EOK;
373 : }
|