]> granicus.if.org Git - shadow/blob - src/userdel.c
* src/newusers.c: Fix typo.
[shadow] / src / userdel.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2011, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <grp.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <stdio.h>
44 #include <sys/stat.h>
45 #include <sys/stat.h>
46 #ifdef ACCT_TOOLS_SETUID
47 #ifdef USE_PAM
48 #include "pam_defs.h"
49 #endif                          /* USE_PAM */
50 #endif                          /* ACCT_TOOLS_SETUID */
51 #include "defines.h"
52 #include "getdef.h"
53 #include "groupio.h"
54 #include "nscd.h"
55 #include "prototypes.h"
56 #include "pwauth.h"
57 #include "pwio.h"
58 #include "shadowio.h"
59 #ifdef  SHADOWGRP
60 #include "sgroupio.h"
61 #endif                          /* SHADOWGRP */
62 #ifdef WITH_TCB
63 #include <tcb.h>
64 #include "tcbfuncs.h"
65 #endif                          /* WITH_TCB */
66 /*@-exitarg@*/
67 #include "exitcodes.h"
68
69 /*
70  * exit status values
71  */
72 #define E_PW_UPDATE     1       /* can't update password file */
73 #define E_NOTFOUND      6       /* specified user doesn't exist */
74 #define E_USER_BUSY     8       /* user currently logged in */
75 #define E_GRP_UPDATE    10      /* can't update group file */
76 #define E_HOMEDIR       12      /* can't remove home directory */
77
78 /*
79  * Global variables
80  */
81 const char *Prog;
82
83 static char *user_name;
84 static uid_t user_id;
85 static gid_t user_gid;
86 static char *user_home;
87
88 static bool fflg = false;
89 static bool rflg = false;
90
91 static bool is_shadow_pwd;
92
93 #ifdef SHADOWGRP
94 static bool is_shadow_grp;
95 static bool sgr_locked = false;
96 #endif                          /* SHADOWGRP */
97 static bool pw_locked  = false;
98 static bool gr_locked   = false;
99 static bool spw_locked  = false;
100
101 /* local function prototypes */
102 static void usage (int status);
103 static void update_groups (void);
104 static void remove_usergroup (void);
105 static void close_files (void);
106 static void fail_exit (int);
107 static void open_files (void);
108 static void update_user (void);
109 static void user_cancel (const char *);
110
111 #ifdef EXTRA_CHECK_HOME_DIR
112 static bool path_prefix (const char *, const char *);
113 #endif                          /* EXTRA_CHECK_HOME_DIR */
114 static int is_owner (uid_t, const char *);
115 static int remove_mailbox (void);
116 #ifdef WITH_TCB
117 static int remove_tcbdir (const char *user_name, uid_t user_id);
118 #endif                          /* WITH_TCB */
119
120 /*
121  * usage - display usage message and exit
122  */
123 static void usage (int status)
124 {
125         fputs (_("Usage: userdel [options] LOGIN\n"
126                  "\n"
127                  "Options:\n"
128                  "  -f, --force                   force removal of files,\n"
129                  "                                even if not owned by user\n"
130                  "  -h, --help                    display this help message and exit\n"
131                  "  -r, --remove                  remove home directory and mail spool\n"
132                  "\n"), (E_SUCCESS != status) ? stderr : stdout);
133         exit (status);
134 }
135
136 /*
137  * update_groups - delete user from secondary group set
138  *
139  *      update_groups() takes the user name that was given and searches
140  *      the group files for membership in any group.
141  *
142  *      we also check to see if they have any groups they own (the same
143  *      name is their user name) and delete them too (only if USERGROUPS_ENAB
144  *      is enabled).
145  */
146 static void update_groups (void)
147 {
148         const struct group *grp;
149         struct group *ngrp;
150
151 #ifdef  SHADOWGRP
152         const struct sgrp *sgrp;
153         struct sgrp *nsgrp;
154 #endif                          /* SHADOWGRP */
155
156         /*
157          * Scan through the entire group file looking for the groups that
158          * the user is a member of.
159          */
160         for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) {
161
162                 /*
163                  * See if the user specified this group as one of their
164                  * concurrent groups.
165                  */
166                 if (!is_on_list (grp->gr_mem, user_name)) {
167                         continue;
168                 }
169
170                 /*
171                  * Delete the username from the list of group members and
172                  * update the group entry to reflect the change.
173                  */
174                 ngrp = __gr_dup (grp);
175                 if (NULL == ngrp) {
176                         fprintf (stderr,
177                                  _("%s: Out of memory. Cannot update %s.\n"),
178                                  Prog, gr_dbname ());
179                         exit (13);      /* XXX */
180                 }
181                 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
182                 if (gr_update (ngrp) == 0) {
183                         fprintf (stderr,
184                                  _("%s: failed to prepare the new %s entry '%s'\n"),
185                                  Prog, gr_dbname (), ngrp->gr_name);
186                         exit (E_GRP_UPDATE);
187                 }
188
189                 /*
190                  * Update the DBM group file with the new entry as well.
191                  */
192 #ifdef WITH_AUDIT
193                 audit_logger (AUDIT_DEL_USER, Prog,
194                               "deleting user from group",
195                               user_name, (unsigned int) user_id,
196                               SHADOW_AUDIT_SUCCESS);
197 #endif                          /* WITH_AUDIT */
198                 SYSLOG ((LOG_INFO, "delete '%s' from group '%s'\n",
199                          user_name, ngrp->gr_name));
200         }
201
202         if (getdef_bool ("USERGROUPS_ENAB")) {
203                 remove_usergroup ();
204         }
205
206 #ifdef  SHADOWGRP
207         if (!is_shadow_grp) {
208                 return;
209         }
210
211         /*
212          * Scan through the entire shadow group file looking for the groups
213          * that the user is a member of. Both the administrative list and
214          * the ordinary membership list is checked.
215          */
216         for (sgr_rewind (), sgrp = sgr_next ();
217              NULL != sgrp;
218              sgrp = sgr_next ()) {
219                 bool was_member, was_admin;
220
221                 /*
222                  * See if the user specified this group as one of their
223                  * concurrent groups.
224                  */
225                 was_member = is_on_list (sgrp->sg_mem, user_name);
226                 was_admin = is_on_list (sgrp->sg_adm, user_name);
227
228                 if (!was_member && !was_admin) {
229                         continue;
230                 }
231
232                 nsgrp = __sgr_dup (sgrp);
233                 if (NULL == nsgrp) {
234                         fprintf (stderr,
235                                  _("%s: Out of memory. Cannot update %s.\n"),
236                                  Prog, sgr_dbname ());
237                         exit (13);      /* XXX */
238                 }
239
240                 if (was_member) {
241                         nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
242                 }
243
244                 if (was_admin) {
245                         nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
246                 }
247
248                 if (sgr_update (nsgrp) == 0) {
249                         fprintf (stderr,
250                                  _("%s: failed to prepare the new %s entry '%s'\n"),
251                                  Prog, sgr_dbname (), nsgrp->sg_name);
252                         exit (E_GRP_UPDATE);
253                 }
254 #ifdef WITH_AUDIT
255                 audit_logger (AUDIT_DEL_USER, Prog,
256                               "deleting user from shadow group",
257                               user_name, (unsigned int) user_id,
258                               SHADOW_AUDIT_SUCCESS);
259 #endif                          /* WITH_AUDIT */
260                 SYSLOG ((LOG_INFO, "delete '%s' from shadow group '%s'\n",
261                          user_name, nsgrp->sg_name));
262         }
263 #endif                          /* SHADOWGRP */
264 }
265
266 /*
267  * remove_usergroup - delete the user's group if it is a usergroup
268  *
269  *      An usergroup is removed if
270  *        + it has the same name as the user
271  *        + it is the primary group of the user
272  *        + it has no other members
273  *        + it is not the primary group of any other user
274  */
275 static void remove_usergroup (void)
276 {
277         const struct group *grp;
278         const struct passwd *pwd = NULL;
279
280         grp = gr_locate (user_name);
281         if (NULL == grp) {
282                 /* This user has no usergroup. */
283                 return;
284         }
285
286         if (grp->gr_gid != user_gid) {
287                 fprintf (stderr,
288                          _("%s: group %s not removed because it is not the primary group of user %s.\n"),
289                          Prog, grp->gr_name, user_name);
290                 return;
291         }
292
293         if (NULL != grp->gr_mem[0]) {
294                 /* The usergroup has other members. */
295                 fprintf (stderr,
296                          _("%s: group %s not removed because it has other members.\n"),
297                          Prog, grp->gr_name);
298                 return;
299         }
300
301         if (!fflg) {
302                 /*
303                  * Scan the passwd file to check if this group is still
304                  * used as a primary group.
305                  */
306                 setpwent ();
307                 while ((pwd = getpwent ()) != NULL) {
308                         if (strcmp (pwd->pw_name, user_name) == 0) {
309                                 continue;
310                         }
311                         if (pwd->pw_gid == grp->gr_gid) {
312                                 fprintf (stderr,
313                                          _("%s: group %s is the primary group of another user and is not removed.\n"),
314                                          Prog, grp->gr_name);
315                                 break;
316                         }
317                 }
318                 endpwent ();
319         }
320
321         if (NULL == pwd) {
322                 /*
323                  * We can remove this group, it is not the primary
324                  * group of any remaining user.
325                  */
326                 if (gr_remove (grp->gr_name) == 0) {
327                         fprintf (stderr,
328                                  _("%s: cannot remove entry '%s' from %s\n"),
329                                  Prog, grp->gr_name, gr_dbname ());
330                         fail_exit (E_GRP_UPDATE);
331                 }
332
333 #ifdef WITH_AUDIT
334                 audit_logger (AUDIT_DEL_GROUP, Prog,
335                               "deleting group",
336                               grp->gr_name, AUDIT_NO_ID,
337                               SHADOW_AUDIT_SUCCESS);
338 #endif                          /* WITH_AUDIT */
339                 SYSLOG ((LOG_INFO,
340                          "removed group '%s' owned by '%s'\n",
341                          grp->gr_name, user_name));
342
343 #ifdef  SHADOWGRP
344                 if (sgr_locate (user_name) != NULL) {
345                         if (sgr_remove (user_name) == 0) {
346                                 fprintf (stderr,
347                                          _("%s: cannot remove entry '%s' from %s\n"),
348                                          Prog, user_name, sgr_dbname ());
349                                 fail_exit (E_GRP_UPDATE);
350                         }
351 #ifdef WITH_AUDIT
352                         audit_logger (AUDIT_DEL_GROUP, Prog,
353                                       "deleting shadow group",
354                                       grp->gr_name, AUDIT_NO_ID,
355                                       SHADOW_AUDIT_SUCCESS);
356 #endif                          /* WITH_AUDIT */
357                         SYSLOG ((LOG_INFO,
358                                  "removed shadow group '%s' owned by '%s'\n",
359                                  grp->gr_name, user_name));
360
361                 }
362 #endif                          /* SHADOWGRP */
363         }
364 }
365
366 /*
367  * close_files - close all of the files that were opened
368  *
369  *      close_files() closes all of the files that were opened for this
370  *      new user. This causes any modified entries to be written out.
371  */
372 static void close_files (void)
373 {
374         if (pw_close () == 0) {
375                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
376                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
377                 fail_exit (E_PW_UPDATE);
378         }
379         if (pw_unlock () == 0) {
380                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
381                 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
382                 /* continue */
383         }
384         pw_locked = false;
385
386         if (is_shadow_pwd) {
387                 if (spw_close () == 0) {
388                         fprintf (stderr,
389                                  _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
390                         SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
391                         fail_exit (E_PW_UPDATE);
392                 }
393                 if (spw_unlock () == 0) {
394                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
395                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
396                         /* continue */
397                 }
398                 spw_locked = false;
399         }
400
401         if (gr_close () == 0) {
402                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
403                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
404                 fail_exit (E_GRP_UPDATE);
405         }
406         if (gr_unlock () == 0) {
407                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
408                 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
409                 /* continue */
410         }
411         gr_locked = false;
412
413 #ifdef  SHADOWGRP
414         if (is_shadow_grp) {
415                 if (sgr_close () == 0) {
416                         fprintf (stderr,
417                                  _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
418                         SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
419                         fail_exit (E_GRP_UPDATE);
420                 }
421
422                 if (sgr_unlock () == 0) {
423                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
424                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
425                         /* continue */
426                 }
427                 sgr_locked = false;
428         }
429 #endif                          /* SHADOWGRP */
430 }
431
432 /*
433  * fail_exit - exit with a failure code after unlocking the files
434  */
435 static void fail_exit (int code)
436 {
437         if (pw_locked) {
438                 if (pw_unlock () == 0) {
439                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
440                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
441                         /* continue */
442                 }
443         }
444         if (gr_locked) {
445                 if (gr_unlock () == 0) {
446                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
447                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
448                         /* continue */
449                 }
450         }
451         if (spw_locked) {
452                 if (spw_unlock () == 0) {
453                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
454                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
455                         /* continue */
456                 }
457         }
458 #ifdef  SHADOWGRP
459         if (sgr_locked) {
460                 if (sgr_unlock () == 0) {
461                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
462                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
463                         /* continue */
464                 }
465         }
466 #endif                          /* SHADOWGRP */
467
468 #ifdef WITH_AUDIT
469         audit_logger (AUDIT_DEL_USER, Prog,
470                       "deleting user",
471                       user_name, (unsigned int) user_id,
472                       SHADOW_AUDIT_FAILURE);
473 #endif                          /* WITH_AUDIT */
474
475         exit (code);
476 }
477
478 /*
479  * open_files - lock and open the password files
480  *
481  *      open_files() opens the two password files.
482  */
483
484 static void open_files (void)
485 {
486         if (pw_lock () == 0) {
487                 fprintf (stderr,
488                          _("%s: cannot lock %s; try again later.\n"),
489                          Prog, pw_dbname ());
490 #ifdef WITH_AUDIT
491                 audit_logger (AUDIT_DEL_USER, Prog,
492                               "locking password file",
493                               user_name, (unsigned int) user_id,
494                               SHADOW_AUDIT_FAILURE);
495 #endif                          /* WITH_AUDIT */
496                 fail_exit (E_PW_UPDATE);
497         }
498         pw_locked = true;
499         if (pw_open (O_RDWR) == 0) {
500                 fprintf (stderr,
501                          _("%s: cannot open %s\n"), Prog, pw_dbname ());
502 #ifdef WITH_AUDIT
503                 audit_logger (AUDIT_DEL_USER, Prog,
504                               "opening password file",
505                               user_name, (unsigned int) user_id,
506                               SHADOW_AUDIT_FAILURE);
507 #endif                          /* WITH_AUDIT */
508                 fail_exit (E_PW_UPDATE);
509         }
510         if (is_shadow_pwd) {
511                 if (spw_lock () == 0) {
512                         fprintf (stderr,
513                                  _("%s: cannot lock %s; try again later.\n"),
514                                  Prog, spw_dbname ());
515 #ifdef WITH_AUDIT
516                         audit_logger (AUDIT_DEL_USER, Prog,
517                                       "locking shadow password file",
518                                       user_name, (unsigned int) user_id,
519                                       SHADOW_AUDIT_FAILURE);
520 #endif                          /* WITH_AUDIT */
521                         fail_exit (E_PW_UPDATE);
522                 }
523                 spw_locked = true;
524                 if (spw_open (O_RDWR) == 0) {
525                         fprintf (stderr,
526                                  _("%s: cannot open %s\n"),
527                                  Prog, spw_dbname ());
528 #ifdef WITH_AUDIT
529                         audit_logger (AUDIT_DEL_USER, Prog,
530                                       "opening shadow password file",
531                                       user_name, (unsigned int) user_id,
532                                       SHADOW_AUDIT_FAILURE);
533 #endif                          /* WITH_AUDIT */
534                         fail_exit (E_PW_UPDATE);
535                 }
536         }
537         if (gr_lock () == 0) {
538                 fprintf (stderr,
539                          _("%s: cannot lock %s; try again later.\n"),
540                          Prog, gr_dbname ());
541 #ifdef WITH_AUDIT
542                 audit_logger (AUDIT_DEL_USER, Prog,
543                               "locking group file",
544                               user_name, (unsigned int) user_id,
545                               SHADOW_AUDIT_FAILURE);
546 #endif                          /* WITH_AUDIT */
547                 fail_exit (E_GRP_UPDATE);
548         }
549         gr_locked = true;
550         if (gr_open (O_RDWR) == 0) {
551                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
552 #ifdef WITH_AUDIT
553                 audit_logger (AUDIT_DEL_USER, Prog,
554                               "opening group file",
555                               user_name, (unsigned int) user_id,
556                               SHADOW_AUDIT_FAILURE);
557 #endif                          /* WITH_AUDIT */
558                 fail_exit (E_GRP_UPDATE);
559         }
560 #ifdef  SHADOWGRP
561         if (is_shadow_grp) {
562                 if (sgr_lock () == 0) {
563                         fprintf (stderr,
564                                  _("%s: cannot lock %s; try again later.\n"),
565                                  Prog, sgr_dbname ());
566 #ifdef WITH_AUDIT
567                         audit_logger (AUDIT_DEL_USER, Prog,
568                                       "locking shadow group file",
569                                       user_name, (unsigned int) user_id,
570                                       SHADOW_AUDIT_FAILURE);
571 #endif                          /* WITH_AUDIT */
572                         fail_exit (E_GRP_UPDATE);
573                 }
574                 sgr_locked= true;
575                 if (sgr_open (O_RDWR) == 0) {
576                         fprintf (stderr, _("%s: cannot open %s\n"),
577                                  Prog, sgr_dbname ());
578 #ifdef WITH_AUDIT
579                         audit_logger (AUDIT_DEL_USER, Prog,
580                                       "opening shadow group file",
581                                       user_name, (unsigned int) user_id,
582                                       SHADOW_AUDIT_FAILURE);
583 #endif                          /* WITH_AUDIT */
584                         fail_exit (E_GRP_UPDATE);
585                 }
586         }
587 #endif                          /* SHADOWGRP */
588 }
589
590 /*
591  * update_user - delete the user entries
592  *
593  *      update_user() deletes the password file entries for this user
594  *      and will update the group entries as required.
595  */
596 static void update_user (void)
597 {
598         if (pw_remove (user_name) == 0) {
599                 fprintf (stderr,
600                          _("%s: cannot remove entry '%s' from %s\n"),
601                          Prog, user_name, pw_dbname ());
602                 fail_exit (E_PW_UPDATE);
603         }
604         if (   is_shadow_pwd
605             && (spw_locate (user_name) != NULL)
606             && (spw_remove (user_name) == 0)) {
607                 fprintf (stderr,
608                          _("%s: cannot remove entry '%s' from %s\n"),
609                          Prog, user_name, spw_dbname ());
610                 fail_exit (E_PW_UPDATE);
611         }
612 #ifdef WITH_AUDIT
613         audit_logger (AUDIT_DEL_USER, Prog,
614                       "deleting user entries",
615                       user_name, (unsigned int) user_id,
616                       SHADOW_AUDIT_SUCCESS);
617 #endif                          /* WITH_AUDIT */
618         SYSLOG ((LOG_INFO, "delete user '%s'\n", user_name));
619 }
620
621 /*
622  * user_cancel - cancel cron and at jobs
623  *
624  *      user_cancel calls a script for additional cleanups like removal of
625  *      cron, at, or print jobs.
626  */
627
628 static void user_cancel (const char *user)
629 {
630         const char *cmd;
631         const char *argv[3];
632         int status;
633
634         cmd = getdef_str ("USERDEL_CMD");
635         if (NULL == cmd) {
636                 return;
637         }
638         argv[0] = cmd;
639         argv[1] = user;
640         argv[2] = (char *)0;
641         (void) run_command (cmd, argv, NULL, &status);
642 }
643
644 #ifdef EXTRA_CHECK_HOME_DIR
645 static bool path_prefix (const char *s1, const char *s2)
646 {
647         return (   (strncmp (s2, s1, strlen (s1)) == 0)
648                 && (   ('\0' == s2[strlen (s1)])
649                     || ('/'  == s2[strlen (s1)])));
650 }
651 #endif                          /* EXTRA_CHECK_HOME_DIR */
652
653 /*
654  * is_owner - Check if path is owned by uid
655  *
656  * Return
657  *  1: path exists and is owned by uid
658  *  0: path is not owned by uid, or a failure occurred
659  * -1: path does not exist
660  */
661 static int is_owner (uid_t uid, const char *path)
662 {
663         struct stat st;
664
665         errno = 0;
666         if (stat (path, &st) != 0) {
667                 if ((ENOENT == errno) || (ENOTDIR == errno)) {
668                         /* The file or directory does not exist */
669                         return -1;
670                 } else {
671                         return 0;
672                 }
673         }
674         return (st.st_uid == uid) ? 1 : 0;
675 }
676
677 static int remove_mailbox (void)
678 {
679         const char *maildir;
680         char mailfile[1024];
681         int i;
682         int errors = 0;
683
684         maildir = getdef_str ("MAIL_DIR");
685 #ifdef MAIL_SPOOL_DIR
686         if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
687                 maildir = MAIL_SPOOL_DIR;
688         }
689 #endif                          /* MAIL_SPOOL_DIR */
690         if (NULL == maildir) {
691                 return 0;
692         }
693         snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
694
695         if (access (mailfile, F_OK) != 0) {
696                 if (ENOENT == errno) {
697                         fprintf (stderr,
698                                  _("%s: %s mail spool (%s) not found\n"),
699                                  Prog, user_name, mailfile);
700                         return 0;
701                 } else {
702                         fprintf (stderr,
703                                  _("%s: warning: can't remove %s: %s\n"),
704                                  Prog, mailfile, strerror (errno));
705                         SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
706 #ifdef WITH_AUDIT
707                         audit_logger (AUDIT_DEL_USER, Prog,
708                                       "deleting mail file",
709                                       user_name, (unsigned int) user_id,
710                                       SHADOW_AUDIT_FAILURE);
711 #endif                          /* WITH_AUDIT */
712                         return -1;
713                 }
714         }
715
716         if (fflg) {
717                 if (unlink (mailfile) != 0) {
718                         fprintf (stderr,
719                                  _("%s: warning: can't remove %s: %s\n"),
720                                  Prog, mailfile, strerror (errno));
721                         SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
722 #ifdef WITH_AUDIT
723                         audit_logger (AUDIT_DEL_USER, Prog,
724                                       "deleting mail file",
725                                       user_name, (unsigned int) user_id,
726                                       SHADOW_AUDIT_FAILURE);
727 #endif                          /* WITH_AUDIT */
728                         errors = 1;
729                         /* continue */
730                 }
731 #ifdef WITH_AUDIT
732                 else
733                 {
734                         audit_logger (AUDIT_DEL_USER, Prog,
735                                       "deleting mail file",
736                                       user_name, (unsigned int) user_id,
737                                       SHADOW_AUDIT_SUCCESS);
738                 }
739 #endif                          /* WITH_AUDIT */
740                 return errors;
741         }
742         i = is_owner (user_id, mailfile);
743         if (i == 0) {
744                 fprintf (stderr,
745                          _("%s: %s not owned by %s, not removing\n"),
746                          Prog, mailfile, user_name);
747                 SYSLOG ((LOG_ERR,
748                          "%s not owned by %s, not removed",
749                          mailfile, strerror (errno)));
750 #ifdef WITH_AUDIT
751                 audit_logger (AUDIT_DEL_USER, Prog,
752                               "deleting mail file",
753                               user_name, (unsigned int) user_id,
754                               SHADOW_AUDIT_FAILURE);
755 #endif                          /* WITH_AUDIT */
756                 return 1;
757         } else if (i == -1) {
758                 return 0;               /* mailbox doesn't exist */
759         }
760         if (unlink (mailfile) != 0) {
761                 fprintf (stderr,
762                          _("%s: warning: can't remove %s: %s\n"),
763                          Prog, mailfile, strerror (errno));
764                 SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
765 #ifdef WITH_AUDIT
766                 audit_logger (AUDIT_DEL_USER, Prog,
767                               "deleting mail file",
768                               user_name, (unsigned int) user_id,
769                               SHADOW_AUDIT_FAILURE);
770 #endif                          /* WITH_AUDIT */
771                 errors = 1;
772                 /* continue */
773         }
774 #ifdef WITH_AUDIT
775         else
776         {
777                 audit_logger (AUDIT_DEL_USER, Prog,
778                               "deleting mail file",
779                               user_name, (unsigned int) user_id,
780                               SHADOW_AUDIT_SUCCESS);
781         }
782 #endif                          /* WITH_AUDIT */
783         return errors;
784 }
785
786 #ifdef WITH_TCB
787 static int remove_tcbdir (const char *user_name, uid_t user_id)
788 {
789         char *buf;
790         int ret = 0;
791         size_t bufsize = (sizeof TCB_DIR) + strlen (user_name) + 2;
792
793         if (!getdef_bool ("USE_TCB")) {
794                 return 0;
795         }
796
797         buf = malloc (buflen);
798         if (NULL == buf) {
799                 fprintf (stderr, _("%s: Can't allocate memory, "
800                                    "tcb entry for %s not removed.\n"),
801                          Prog, user_name);
802                 return 1;
803         }
804         snprintf (buf, buflen, TCB_DIR "/%s", user_name);
805         if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
806                 fprintf (stderr, _("%s: Cannot drop privileges: %s\n"),
807                          Prog, strerror (errno));
808                 shadowtcb_gain_priv ();
809                 free (buf);
810                 return 1;
811         }
812         /* Only remove directory contents with dropped privileges.
813          * We will regain them and remove the user's tcb directory afterwards.
814          */
815         if (remove_tree (buf, false) != 0) {
816                 fprintf (stderr, _("%s: Cannot remove the content of %s: %s\n"),
817                          Prog, buf, strerror (errno));
818                 shadowtcb_gain_priv ();
819                 free (buf);
820                 return 1;
821         }
822         shadowtcb_gain_priv ();
823         free (buf);
824         if (shadowtcb_remove (user_name) == SHADOWTCB_FAILURE) {
825                 fprintf (stderr, _("%s: Cannot remove tcb files for %s: %s\n"),
826                          Prog, user_name, strerror (errno));
827                 ret = 1;
828         }
829         return ret;
830 }
831 #endif                          /* WITH_TCB */
832
833 /*
834  * main - userdel command
835  */
836 int main (int argc, char **argv)
837 {
838         int errors = 0; /* Error in the removal of the home directory */
839
840 #ifdef ACCT_TOOLS_SETUID
841 #ifdef USE_PAM
842         pam_handle_t *pamh = NULL;
843         int retval;
844 #endif                          /* USE_PAM */
845 #endif                          /* ACCT_TOOLS_SETUID */
846
847 #ifdef WITH_AUDIT
848         audit_help_open ();
849 #endif                          /* WITH_AUDIT */
850
851         /*
852          * Get my name so that I can use it to report errors.
853          */
854         Prog = Basename (argv[0]);
855         (void) setlocale (LC_ALL, "");
856         (void) bindtextdomain (PACKAGE, LOCALEDIR);
857         (void) textdomain (PACKAGE);
858
859         {
860                 /*
861                  * Parse the command line options.
862                  */
863                 int c;
864                 static struct option long_options[] = {
865                         {"force", no_argument, NULL, 'f'},
866                         {"help", no_argument, NULL, 'h'},
867                         {"remove", no_argument, NULL, 'r'},
868                         {NULL, 0, NULL, '\0'}
869                 };
870                 while ((c = getopt_long (argc, argv, "fhr",
871                                          long_options, NULL)) != -1) {
872                         switch (c) {
873                         case 'f':       /* force remove even if not owned by user */
874                                 fflg = true;
875                                 break;
876                         case 'h':
877                                 usage (E_SUCCESS);
878                                 break;
879                         case 'r':       /* remove home dir and mailbox */
880                                 rflg = true;
881                                 break;
882                         default:
883                                 usage (E_USAGE);
884                         }
885                 }
886         }
887
888         if ((optind + 1) != argc) {
889                 usage (E_USAGE);
890         }
891
892         OPENLOG ("userdel");
893
894 #ifdef ACCT_TOOLS_SETUID
895 #ifdef USE_PAM
896         {
897                 struct passwd *pampw;
898                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
899                 if (pampw == NULL) {
900                         fprintf (stderr,
901                                  _("%s: Cannot determine your user name.\n"),
902                                  Prog);
903                         exit (E_PW_UPDATE);
904                 }
905
906                 retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh);
907         }
908
909         if (PAM_SUCCESS == retval) {
910                 retval = pam_authenticate (pamh, 0);
911         }
912
913         if (PAM_SUCCESS == retval) {
914                 retval = pam_acct_mgmt (pamh, 0);
915         }
916
917         if (NULL != pamh) {
918                 (void) pam_end (pamh, retval);
919         }
920         if (PAM_SUCCESS != retval) {
921                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
922                 exit (E_PW_UPDATE);
923         }
924 #endif                          /* USE_PAM */
925 #endif                          /* ACCT_TOOLS_SETUID */
926
927         is_shadow_pwd = spw_file_present ();
928 #ifdef SHADOWGRP
929         is_shadow_grp = sgr_file_present ();
930 #endif                          /* SHADOWGRP */
931
932         /*
933          * Start with a quick check to see if the user exists.
934          */
935         user_name = argv[argc - 1];
936         {
937                 struct passwd *pwd;
938                 pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
939                 if (NULL == pwd) {
940                         fprintf (stderr, _("%s: user '%s' does not exist\n"),
941                                  Prog, user_name);
942 #ifdef WITH_AUDIT
943                         audit_logger (AUDIT_DEL_USER, Prog,
944                                       "deleting user not found",
945                                       user_name, AUDIT_NO_ID,
946                                       SHADOW_AUDIT_FAILURE);
947 #endif                          /* WITH_AUDIT */
948                         exit (E_NOTFOUND);
949                 }
950                 user_id = pwd->pw_uid;
951                 user_gid = pwd->pw_gid;
952                 user_home = xstrdup (pwd->pw_dir);
953         }
954 #ifdef WITH_TCB
955         if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
956                 exit (E_NOTFOUND);
957         }
958 #endif                          /* WITH_TCB */
959 #ifdef  USE_NIS
960
961         /*
962          * Now make sure it isn't an NIS user.
963          */
964         if (__ispwNIS ()) {
965                 char *nis_domain;
966                 char *nis_master;
967
968                 fprintf (stderr,
969                          _("%s: user %s is a NIS user\n"), Prog, user_name);
970                 if (   !yp_get_default_domain (&nis_domain)
971                     && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
972                         fprintf (stderr,
973                                  _("%s: %s is the NIS master\n"),
974                                  Prog, nis_master);
975                 }
976                 exit (E_NOTFOUND);
977         }
978 #endif                          /* USE_NIS */
979         /*
980          * Check to make certain the user isn't logged in.
981          * Note: This is a best effort basis. The user may log in between,
982          * a cron job may be started on her behalf, etc.
983          */
984         if (user_busy (user_name, user_id) != 0) {
985                 if (!fflg) {
986 #ifdef WITH_AUDIT
987                         audit_logger (AUDIT_DEL_USER, Prog,
988                                       "deleting user logged in",
989                                       user_name, AUDIT_NO_ID,
990                                       SHADOW_AUDIT_FAILURE);
991 #endif                          /* WITH_AUDIT */
992                         exit (E_USER_BUSY);
993                 }
994         }
995
996         /*
997          * Do the hard stuff - open the files, create the user entries,
998          * create the home directory, then close and update the files.
999          */
1000         open_files ();
1001         update_user ();
1002         update_groups ();
1003
1004         if (rflg) {
1005                 errors += remove_mailbox ();
1006         }
1007         if (rflg) {
1008                 int home_owned = is_owner (user_id, user_home);
1009                 if (-1 == home_owned) {
1010                         fprintf (stderr,
1011                                  _("%s: %s home directory (%s) not found\n"),
1012                                  Prog, user_name, user_home);
1013                         rflg = 0;
1014                 } else if ((0 == home_owned) && !fflg) {
1015                         fprintf (stderr,
1016                                  _("%s: %s not owned by %s, not removing\n"),
1017                                  Prog, user_home, user_name);
1018                         rflg = 0;
1019                         errors++;
1020                         /* continue */
1021                 }
1022         }
1023
1024 #ifdef EXTRA_CHECK_HOME_DIR
1025         /* This may be slow, the above should be good enough. */
1026         if (rflg && !fflg) {
1027                 struct passwd *pwd;
1028                 /*
1029                  * For safety, refuse to remove the home directory if it
1030                  * would result in removing some other user's home
1031                  * directory. Still not perfect so be careful, but should
1032                  * prevent accidents if someone has /home or / as home
1033                  * directory...  --marekm
1034                  */
1035                 setpwent ();
1036                 while ((pwd = getpwent ())) {
1037                         if (strcmp (pwd->pw_name, user_name) == 0) {
1038                                 continue;
1039                         }
1040                         if (path_prefix (user_home, pwd->pw_dir)) {
1041                                 fprintf (stderr,
1042                                          _("%s: not removing directory %s (would remove home of user %s)\n"),
1043                                          Prog, user_home, pwd->pw_name);
1044                                 rflg = false;
1045                                 errors++;
1046                                 /* continue */
1047                                 break;
1048                         }
1049                 }
1050                 endpwent ();
1051         }
1052 #endif                          /* EXTRA_CHECK_HOME_DIR */
1053
1054         if (rflg) {
1055                 if (remove_tree (user_home, true) != 0) {
1056                         fprintf (stderr,
1057                                  _("%s: error removing directory %s\n"),
1058                                  Prog, user_home);
1059                         errors++;
1060                         /* continue */
1061                 }
1062 #ifdef WITH_AUDIT
1063                 else
1064                 {
1065                         audit_logger (AUDIT_DEL_USER, Prog,
1066                                       "deleting home directory",
1067                                       user_name, (unsigned int) user_id,
1068                                       SHADOW_AUDIT_SUCCESS);
1069                 }
1070 #endif                          /* WITH_AUDIT */
1071         }
1072 #ifdef WITH_AUDIT
1073         if (0 != errors) {
1074                 audit_logger (AUDIT_DEL_USER, Prog,
1075                               "deleting home directory",
1076                               user_name, AUDIT_NO_ID,
1077                               SHADOW_AUDIT_FAILURE);
1078         }
1079 #endif                          /* WITH_AUDIT */
1080
1081 #ifdef WITH_SELINUX
1082         if (is_selinux_enabled () > 0) {
1083                 const char *args[5];
1084                 args[0] = "/usr/sbin/semanage";
1085                 args[1] = "login";
1086                 args[2] = "-d";
1087                 args[3] = user_name;
1088                 args[4] = NULL;
1089                 safe_system (args[0], args, NULL, true);
1090         }
1091 #endif                          /* WITH_SELINUX */
1092
1093         /*
1094          * Cancel any crontabs or at jobs. Have to do this before we remove
1095          * the entry from /etc/passwd.
1096          */
1097         user_cancel (user_name);
1098         close_files ();
1099
1100 #ifdef WITH_TCB
1101         errors += remove_tcbdir (user_name, user_id);
1102 #endif                          /* WITH_TCB */
1103
1104         nscd_flush_cache ("passwd");
1105         nscd_flush_cache ("group");
1106
1107         return ((0 != errors) ? E_HOMEDIR : E_SUCCESS);
1108 }
1109