Line data Source code
1 : /*
2 : SSSD
3 :
4 : tools_utils.c
5 :
6 : Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
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 <tevent.h>
24 : #include <popt.h>
25 : #include <errno.h>
26 : #include <sys/stat.h>
27 : #include <sys/types.h>
28 : #include <sys/wait.h>
29 : #include <fcntl.h>
30 :
31 : #include "config.h"
32 :
33 : #include "util/util.h"
34 : #include "confdb/confdb.h"
35 : #include "db/sysdb.h"
36 : #include "tools/tools_util.h"
37 : #include "tools/sss_sync_ops.h"
38 :
39 0 : static int setup_db(struct tools_ctx *ctx)
40 : {
41 : char *confdb_path;
42 : int ret;
43 :
44 0 : confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
45 0 : if (confdb_path == NULL) {
46 0 : return ENOMEM;
47 : }
48 :
49 : /* Connect to the conf db */
50 0 : ret = confdb_init(ctx, &ctx->confdb, confdb_path);
51 0 : if (ret != EOK) {
52 0 : DEBUG(SSSDBG_CRIT_FAILURE,
53 : "Could not initialize connection to the confdb\n");
54 0 : return ret;
55 : }
56 :
57 0 : ret = sssd_domain_init(ctx, ctx->confdb, "local", DB_PATH, &ctx->local);
58 0 : if (ret != EOK) {
59 0 : SYSDB_VERSION_ERROR(ret);
60 0 : DEBUG(SSSDBG_CRIT_FAILURE,
61 : "Could not initialize connection to the sysdb\n");
62 0 : return ret;
63 : }
64 0 : ctx->sysdb = ctx->local->sysdb;
65 :
66 0 : talloc_free(confdb_path);
67 0 : return EOK;
68 : }
69 :
70 : /*
71 : * Print poptUsage as well as our error message
72 : */
73 0 : void usage(poptContext pc, const char *error)
74 : {
75 : size_t lentmp;
76 0 : char nl[2] = "";
77 :
78 0 : poptPrintUsage(pc, stderr, 0);
79 :
80 0 : if (error) {
81 0 : lentmp = strlen(error);
82 0 : if ((lentmp > 0) && (error[lentmp - 1] != '\n')) {
83 0 : nl[0]='\n';
84 0 : nl[1]='\0';
85 : }
86 :
87 0 : fprintf(stderr, "%s%s", error, nl);
88 : }
89 0 : }
90 :
91 0 : int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out)
92 : {
93 : char **out;
94 : char *orig, *n, *o;
95 0 : char delim = ',';
96 0 : unsigned int tokens = 1;
97 : int i;
98 :
99 0 : orig = talloc_strdup(mem_ctx, optstr);
100 0 : if (!orig) return ENOMEM;
101 :
102 0 : n = orig;
103 0 : tokens = 1;
104 0 : while ((n = strchr(n, delim))) {
105 0 : n++;
106 0 : tokens++;
107 : }
108 :
109 0 : out = talloc_array(mem_ctx, char *, tokens+1);
110 0 : if (!out) {
111 0 : talloc_free(orig);
112 0 : return ENOMEM;
113 : }
114 :
115 0 : n = o = orig;
116 0 : for (i = 0; i < tokens; i++) {
117 0 : o = n;
118 0 : n = strchr(n, delim);
119 0 : if (!n) {
120 0 : break;
121 : }
122 0 : *n = '\0';
123 0 : n++;
124 0 : out[i] = talloc_strdup(out, o);
125 : }
126 0 : out[tokens-1] = talloc_strdup(out, o);
127 0 : out[tokens] = NULL;
128 :
129 0 : talloc_free(orig);
130 0 : *_out = out;
131 0 : return EOK;
132 : }
133 :
134 0 : int parse_group_name_domain(struct tools_ctx *tctx,
135 : char **groups)
136 : {
137 : int i;
138 : int ret;
139 0 : char *name = NULL;
140 0 : char *domain = NULL;
141 :
142 0 : if (!groups) {
143 0 : return EOK;
144 : }
145 :
146 0 : for (i = 0; groups[i]; ++i) {
147 0 : ret = sss_parse_name(tctx, tctx->snctx, groups[i], &domain, &name);
148 0 : if (ret != EOK) {
149 0 : DEBUG(SSSDBG_CRIT_FAILURE,
150 : "Invalid name in group list, skipping: [%s] (%d)\n",
151 : groups[i], ret);
152 0 : continue;
153 : }
154 :
155 : /* If FQDN is specified, it must be within the same domain as user */
156 0 : if (domain) {
157 0 : if (strcmp(domain, tctx->octx->domain->name) != 0) {
158 0 : return EINVAL;
159 : }
160 :
161 : /* Use only groupname */
162 0 : talloc_zfree(groups[i]);
163 0 : groups[i] = talloc_strdup(tctx, name);
164 0 : if (groups[i] == NULL) {
165 0 : return ENOMEM;
166 : }
167 : }
168 :
169 0 : talloc_zfree(name);
170 0 : talloc_zfree(domain);
171 : }
172 :
173 0 : talloc_zfree(name);
174 0 : talloc_zfree(domain);
175 0 : return EOK;
176 : }
177 :
178 0 : int parse_name_domain(struct tools_ctx *tctx,
179 : const char *fullname)
180 : {
181 : int ret;
182 0 : char *domain = NULL;
183 :
184 0 : ret = sss_parse_name(tctx, tctx->snctx, fullname, &domain, &tctx->octx->name);
185 0 : if (ret != EOK) {
186 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot parse full name\n");
187 0 : return ret;
188 : }
189 0 : DEBUG(SSSDBG_FUNC_DATA, "Parsed username: %s\n", tctx->octx->name);
190 :
191 0 : if (domain) {
192 0 : DEBUG(SSSDBG_FUNC_DATA, "Parsed domain: %s\n", domain);
193 : /* only the local domain, whatever named is allowed in tools */
194 0 : if (strcasecmp(domain, tctx->local->name) != 0) {
195 0 : DEBUG(SSSDBG_CRIT_FAILURE,
196 : "Invalid domain %s specified in FQDN\n", domain);
197 0 : return EINVAL;
198 : }
199 : } else {
200 0 : if (tctx->local->fqnames) {
201 0 : DEBUG(SSSDBG_CRIT_FAILURE,
202 : "Name '%s' does not seem to be FQDN "
203 : "('%s = TRUE' is set)\n", fullname, CONFDB_DOMAIN_FQ);
204 0 : ERROR("Name '%1$s' does not seem to be FQDN "
205 : "('%2$s = TRUE' is set)\n", fullname, CONFDB_DOMAIN_FQ);
206 0 : return EINVAL;
207 : }
208 : }
209 :
210 0 : return EOK;
211 : }
212 :
213 0 : int check_group_names(struct tools_ctx *tctx,
214 : char **grouplist,
215 : char **badgroup)
216 : {
217 : int ret;
218 : int i;
219 : struct ops_ctx *groupinfo;
220 :
221 0 : groupinfo = talloc_zero(tctx, struct ops_ctx);
222 0 : if (!groupinfo) {
223 0 : return ENOMEM;
224 : }
225 0 : groupinfo->domain = tctx->local;
226 :
227 0 : ret = EOK;
228 0 : for (i=0; grouplist[i]; ++i) {
229 0 : ret = sysdb_getgrnam_sync(tctx,
230 0 : grouplist[i],
231 : groupinfo);
232 0 : if (ret) {
233 0 : DEBUG(SSSDBG_TRACE_FUNC,
234 : "Cannot find group %s, ret: %d\n", grouplist[i], ret);
235 0 : break;
236 : }
237 : }
238 :
239 0 : talloc_zfree(groupinfo);
240 0 : *badgroup = grouplist[i];
241 0 : return ret;
242 : }
243 :
244 0 : int id_in_range(uint32_t id,
245 : struct sss_domain_info *dom)
246 : {
247 0 : if (id &&
248 0 : ((id < dom->id_min) ||
249 0 : (dom->id_max && id > dom->id_max))) {
250 0 : return ERANGE;
251 : }
252 :
253 0 : return EOK;
254 : }
255 :
256 0 : int set_locale(void)
257 : {
258 : char *c;
259 :
260 0 : c = setlocale(LC_ALL, "");
261 0 : if (c == NULL) {
262 0 : return EIO;
263 : }
264 :
265 0 : errno = 0;
266 0 : c = bindtextdomain(PACKAGE, LOCALEDIR);
267 0 : if (c == NULL) {
268 0 : return errno;
269 : }
270 :
271 0 : errno = 0;
272 0 : c = textdomain(PACKAGE);
273 0 : if (c == NULL) {
274 0 : return errno;
275 : }
276 :
277 0 : return EOK;
278 : }
279 :
280 0 : int init_sss_tools(struct tools_ctx **_tctx)
281 : {
282 : int ret;
283 : struct tools_ctx *tctx;
284 :
285 0 : tctx = talloc_zero(NULL, struct tools_ctx);
286 0 : if (tctx == NULL) {
287 0 : DEBUG(SSSDBG_CRIT_FAILURE,
288 : "Could not allocate memory for tools context\n");
289 0 : return ENOMEM;
290 : }
291 :
292 : /* Connect to the database */
293 0 : ret = setup_db(tctx);
294 0 : if (ret != EOK) {
295 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up database\n");
296 0 : goto fini;
297 : }
298 :
299 0 : ret = sss_names_init(tctx, tctx->confdb, tctx->local->name, &tctx->snctx);
300 0 : if (ret != EOK) {
301 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not set up parsing\n");
302 0 : goto fini;
303 : }
304 :
305 0 : tctx->octx = talloc_zero(tctx, struct ops_ctx);
306 0 : if (!tctx->octx) {
307 0 : DEBUG(SSSDBG_CRIT_FAILURE,
308 : "Could not allocate memory for data context\n");
309 0 : ERROR("Out of memory\n");
310 0 : ret = ENOMEM;
311 0 : goto fini;
312 : }
313 0 : tctx->octx->domain = tctx->local;
314 :
315 0 : *_tctx = tctx;
316 0 : ret = EOK;
317 :
318 : fini:
319 0 : if (ret != EOK) talloc_free(tctx);
320 0 : return ret;
321 : }
322 :
323 : /*
324 : * Check is path is owned by uid
325 : * returns 0 - owns
326 : * -1 - does not own
327 : * >0 - an error occured, error code
328 : */
329 0 : static int is_owner(uid_t uid, const char *path)
330 : {
331 : struct stat statres;
332 : int ret;
333 :
334 0 : ret = stat(path, &statres);
335 0 : if (ret != 0) {
336 0 : ret = errno;
337 0 : DEBUG(SSSDBG_CRIT_FAILURE,
338 : "Cannot stat %s: [%d][%s]\n", path, ret, strerror(ret));
339 0 : return ret;
340 : }
341 :
342 0 : if (statres.st_uid == uid) {
343 0 : return EOK;
344 : }
345 0 : return -1;
346 : }
347 :
348 0 : static int remove_mail_spool(TALLOC_CTX *mem_ctx,
349 : const char *maildir,
350 : const char *username,
351 : uid_t uid,
352 : bool force)
353 : {
354 : int ret;
355 : char *spool_file;
356 :
357 0 : spool_file = talloc_asprintf(mem_ctx, "%s/%s", maildir, username);
358 0 : if (spool_file == NULL) {
359 0 : ret = ENOMEM;
360 0 : goto fail;
361 : }
362 :
363 0 : if (force == false) {
364 : /* Check the owner of the mail spool */
365 0 : ret = is_owner(uid, spool_file);
366 0 : switch (ret) {
367 : case 0:
368 0 : break;
369 : case -1:
370 0 : DEBUG(SSSDBG_MINOR_FAILURE,
371 : "%s not owned by %"SPRIuid", not removing\n",
372 : spool_file, uid);
373 0 : ret = EACCES;
374 : /* FALLTHROUGH */
375 : default:
376 0 : goto fail;
377 : }
378 : }
379 :
380 0 : ret = unlink(spool_file);
381 0 : if (ret != 0) {
382 0 : ret = errno;
383 0 : DEBUG(SSSDBG_CRIT_FAILURE,
384 : "Cannot remove() the spool file %s: [%d][%s]\n",
385 : spool_file, ret, strerror(ret));
386 0 : goto fail;
387 : }
388 :
389 : fail:
390 0 : talloc_free(spool_file);
391 0 : return ret;
392 : }
393 :
394 0 : int remove_homedir(TALLOC_CTX *mem_ctx,
395 : const char *homedir,
396 : const char *maildir,
397 : const char *username,
398 : uid_t uid, bool force)
399 : {
400 : int ret;
401 :
402 0 : ret = remove_mail_spool(mem_ctx, maildir, username, uid, force);
403 0 : if (ret != EOK) {
404 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot remove user's mail spool\n");
405 : /* Should this be fatal? I don't think so. Maybe convert to ERROR? */
406 : }
407 :
408 0 : if (force == false && is_owner(uid, homedir) == -1) {
409 0 : DEBUG(SSSDBG_CRIT_FAILURE,
410 : "Not removing home dir - not owned by user\n");
411 0 : return EPERM;
412 : }
413 :
414 : /* Remove the tree */
415 0 : ret = remove_tree(homedir);
416 0 : if (ret != EOK) {
417 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot remove homedir %s: %d\n",
418 : homedir, ret);
419 0 : return ret;
420 : }
421 :
422 0 : return EOK;
423 : }
424 :
425 : /* The reason for not putting this into create_homedir
426 : * is better granularity when it comes to reporting error
427 : * messages and tracebacks in pysss
428 : */
429 0 : int create_mail_spool(TALLOC_CTX *mem_ctx,
430 : const char *username,
431 : const char *maildir,
432 : uid_t uid, gid_t gid)
433 : {
434 0 : char *spool_file = NULL;
435 0 : int fd = -1;
436 : int ret;
437 :
438 0 : spool_file = talloc_asprintf(mem_ctx, "%s/%s", maildir, username);
439 0 : if (spool_file == NULL) {
440 0 : ret = ENOMEM;
441 0 : goto fail;
442 : }
443 :
444 0 : selinux_file_context(spool_file);
445 :
446 0 : fd = open(spool_file, O_CREAT | O_WRONLY | O_EXCL, 0);
447 0 : if (fd < 0) {
448 0 : ret = errno;
449 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot open() the spool file: [%d][%s]\n",
450 : ret, strerror(ret));
451 0 : goto fail;
452 : }
453 :
454 0 : ret = fchmod(fd, 0600);
455 0 : if (ret != 0) {
456 0 : ret = errno;
457 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot fchmod() the spool file: [%d][%s]\n",
458 : ret, strerror(ret));
459 0 : goto fail;
460 : }
461 :
462 0 : ret = fchown(fd, uid, gid);
463 0 : if (ret != 0) {
464 0 : ret = errno;
465 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot fchown() the spool file: [%d][%s]\n",
466 : ret, strerror(ret));
467 0 : goto fail;
468 : }
469 :
470 0 : ret = fsync(fd);
471 0 : if (ret != 0) {
472 0 : ret = errno;
473 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot fsync() the spool file: [%d][%s]\n",
474 : ret, strerror(ret));
475 : }
476 :
477 : fail:
478 0 : if (fd >= 0) {
479 0 : ret = close(fd);
480 0 : if (ret != 0) {
481 0 : ret = errno;
482 0 : DEBUG(SSSDBG_CRIT_FAILURE,
483 : "Cannot close() the spool file: [%d][%s]\n",
484 : ret, strerror(ret));
485 : }
486 : }
487 :
488 0 : reset_selinux_file_context();
489 0 : talloc_free(spool_file);
490 0 : return ret;
491 : }
492 :
493 0 : int create_homedir(const char *skeldir,
494 : const char *homedir,
495 : uid_t uid,
496 : gid_t gid,
497 : mode_t default_umask)
498 : {
499 : int ret;
500 :
501 0 : selinux_file_context(homedir);
502 :
503 0 : ret = copy_tree(skeldir, homedir, 0777 & ~default_umask, uid, gid);
504 0 : if (ret != EOK) {
505 0 : DEBUG(SSSDBG_CRIT_FAILURE,
506 : "Cannot populate user's home directory: [%d][%s].\n",
507 : ret, strerror(ret));
508 0 : goto done;
509 : }
510 :
511 : done:
512 0 : reset_selinux_file_context();
513 0 : return ret;
514 : }
515 :
516 0 : int run_userdel_cmd(struct tools_ctx *tctx)
517 : {
518 : int ret, status;
519 0 : char *userdel_cmd = NULL;
520 0 : char *conf_path = NULL;
521 : pid_t pid, child_pid;
522 :
523 0 : conf_path = talloc_asprintf(tctx, CONFDB_DOMAIN_PATH_TMPL,
524 0 : tctx->local->name);
525 0 : if (!conf_path) {
526 0 : ret = ENOMEM;
527 0 : goto done;
528 : }
529 :
530 0 : ret = confdb_get_string(tctx->confdb, tctx,
531 : conf_path, CONFDB_LOCAL_USERDEL_CMD,
532 : NULL, &userdel_cmd);
533 0 : if (ret != EOK || !userdel_cmd) {
534 : goto done;
535 : }
536 :
537 0 : errno = 0;
538 0 : pid = fork();
539 0 : if (pid == 0) {
540 : /* child */
541 0 : execl(userdel_cmd, userdel_cmd,
542 0 : tctx->octx->name, (char *) NULL);
543 0 : exit(errno);
544 : } else {
545 : /* parent */
546 0 : if (pid == -1) {
547 0 : ret = errno;
548 0 : DEBUG(SSSDBG_CRIT_FAILURE,
549 : "fork failed [%d]: %s\n", ret, strerror(ret));
550 0 : goto done;
551 : }
552 :
553 0 : while((child_pid = waitpid(pid, &status, 0)) > 0) {
554 0 : if (WIFEXITED(status)) {
555 0 : ret = WEXITSTATUS(status);
556 0 : if (ret != 0) {
557 0 : DEBUG(SSSDBG_FUNC_DATA,
558 : "command [%s] returned nonzero status %d.\n",
559 : userdel_cmd, ret);
560 0 : ret = EOK; /* Ignore return code of the command */
561 0 : goto done;
562 : }
563 0 : } else if (WIFSIGNALED(status)) {
564 0 : DEBUG(SSSDBG_FUNC_DATA,
565 : "command [%s] was terminated by signal %d.\n",
566 : userdel_cmd, WTERMSIG(status));
567 0 : ret = EIO;
568 0 : goto done;
569 0 : } else if (WIFSTOPPED(status)) {
570 0 : DEBUG(SSSDBG_FUNC_DATA,
571 : "command [%s] was stopped by signal %d.\n",
572 : userdel_cmd, WSTOPSIG(status));
573 0 : continue;
574 : } else {
575 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown status from WAITPID\n");
576 0 : ret = EIO;
577 0 : goto done;
578 : }
579 : }
580 0 : if (child_pid == -1) {
581 0 : DEBUG(SSSDBG_CRIT_FAILURE, "waitpid failed\n");
582 0 : ret = errno;
583 0 : goto done;
584 : }
585 : }
586 :
587 0 : ret = EOK;
588 : done:
589 0 : talloc_free(userdel_cmd);
590 0 : talloc_free(conf_path);
591 0 : return ret;
592 : }
593 :
594 0 : static pid_t parse_pid(const char *strpid)
595 : {
596 : long value;
597 : char *endptr;
598 :
599 0 : errno = 0;
600 0 : value = strtol(strpid, &endptr, 10);
601 0 : if ((errno != 0) || (endptr == strpid)
602 0 : || ((*endptr != '\0') && (*endptr != '\n'))) {
603 0 : return 0;
604 : }
605 :
606 0 : return value;
607 : }
608 :
609 0 : static errno_t get_sssd_pid(pid_t *out_pid)
610 : {
611 : int ret;
612 : size_t fsize;
613 0 : FILE *pid_file = NULL;
614 0 : char pid_str[MAX_PID_LENGTH] = {'\0'};
615 :
616 0 : *out_pid = 0;
617 :
618 0 : errno = 0;
619 0 : pid_file = fopen(SSSD_PIDFILE, "r");
620 0 : if (pid_file == NULL) {
621 0 : ret = errno;
622 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Unable to open pid file \"%s\": %s\n",
623 : SSSD_PIDFILE, strerror(ret));
624 0 : goto done;
625 : }
626 :
627 0 : fsize = fread(pid_str, sizeof(char), MAX_PID_LENGTH * sizeof(char),
628 : pid_file);
629 0 : if (!feof(pid_file)) {
630 : /* eof not reached */
631 0 : ret = ferror(pid_file);
632 0 : if (ret != 0) {
633 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read from file \"%s\": %s\n",
634 : SSSD_PIDFILE, strerror(ret));
635 : } else {
636 0 : DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains invalid pid.\n",
637 : SSSD_PIDFILE);
638 : }
639 0 : goto done;
640 : }
641 0 : if (fsize == 0) {
642 0 : DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains no pid.\n",
643 : SSSD_PIDFILE);
644 0 : ret = EINVAL;
645 0 : goto done;
646 : }
647 :
648 0 : pid_str[MAX_PID_LENGTH-1] = '\0';
649 0 : *out_pid = parse_pid(pid_str);
650 0 : if (*out_pid == 0) {
651 0 : DEBUG(SSSDBG_CRIT_FAILURE,
652 : "File \"%s\" contains invalid pid.\n", SSSD_PIDFILE);
653 0 : ret = EINVAL;
654 0 : goto done;
655 : }
656 :
657 0 : ret = EOK;
658 :
659 : done:
660 0 : if (pid_file != NULL) {
661 0 : fclose(pid_file);
662 : }
663 0 : return ret;
664 : }
665 :
666 0 : errno_t signal_sssd(int signum)
667 : {
668 : int ret;
669 : pid_t pid;
670 :
671 0 : ret = get_sssd_pid(&pid);
672 0 : if (ret != EOK) {
673 0 : return ret;
674 : }
675 :
676 0 : if (kill(pid, signum) != 0) {
677 0 : ret = errno;
678 0 : DEBUG(SSSDBG_CRIT_FAILURE,
679 : "Could not send signal %d to process %d: %s\n",
680 : signum, pid, strerror(errno));
681 0 : return ret;
682 : }
683 :
684 0 : return EOK;
685 : }
|