Initial commit for Tizen
[profile/extras/shadow-utils.git] / 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 - 2008, 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: userdel.c 2979 2009-05-22 10:41:10Z nekral-guest $"
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
62 /*@-exitarg@*/
63 #include "exitcodes.h"
64
65 /*
66  * exit status values
67  */
68 #define E_PW_UPDATE     1       /* can't update password file */
69 #define E_NOTFOUND      6       /* specified user doesn't exist */
70 #define E_USER_BUSY     8       /* user currently logged in */
71 #define E_GRP_UPDATE    10      /* can't update group file */
72 #define E_HOMEDIR       12      /* can't remove home directory */
73
74 /*
75  * Global variables
76  */
77 char *Prog;
78
79 static char *user_name;
80 static uid_t user_id;
81 static char *user_home;
82
83 static bool fflg = false;
84 static bool rflg = false;
85
86 static bool is_shadow_pwd;
87
88 #ifdef SHADOWGRP
89 static bool is_shadow_grp;
90 static bool sgr_locked = false;
91 #endif
92 static bool pw_locked  = false;
93 static bool gr_locked   = false;
94 static bool spw_locked  = false;
95
96 /* local function prototypes */
97 static void usage (void);
98 static void update_groups (void);
99 static void close_files (void);
100 static void fail_exit (int);
101 static void open_files (void);
102 static void update_user (void);
103 static void user_cancel (const char *);
104
105 #ifdef EXTRA_CHECK_HOME_DIR
106 static bool path_prefix (const char *, const char *);
107 #endif
108 static int is_owner (uid_t, const char *);
109 static int remove_mailbox (void);
110
111 /*
112  * usage - display usage message and exit
113  */
114 static void usage (void)
115 {
116         fputs (_("Usage: userdel [options] LOGIN\n"
117                  "\n"
118                  "Options:\n"
119                  "  -f, --force                   force removal of files,\n"
120                  "                                even if not owned by user\n"
121                  "  -h, --help                    display this help message and exit\n"
122                  "  -r, --remove                  remove home directory and mail spool\n"
123                  "\n"), stderr);
124         exit (E_USAGE);
125 }
126
127 /*
128  * update_groups - delete user from secondary group set
129  *
130  *      update_groups() takes the user name that was given and searches
131  *      the group files for membership in any group.
132  *
133  *      we also check to see if they have any groups they own (the same
134  *      name is their user name) and delete them too (only if USERGROUPS_ENAB
135  *      is enabled).
136  */
137 static void update_groups (void)
138 {
139         const struct group *grp;
140         struct group *ngrp;
141         struct passwd *pwd;
142
143 #ifdef  SHADOWGRP
144         bool deleted_user_group = false;
145         const struct sgrp *sgrp;
146         struct sgrp *nsgrp;
147 #endif                          /* SHADOWGRP */
148
149         /*
150          * Scan through the entire group file looking for the groups that
151          * the user is a member of.
152          */
153         for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) {
154
155                 /*
156                  * See if the user specified this group as one of their
157                  * concurrent groups.
158                  */
159                 if (!is_on_list (grp->gr_mem, user_name)) {
160                         continue;
161                 }
162
163                 /* 
164                  * Delete the username from the list of group members and
165                  * update the group entry to reflect the change.
166                  */
167                 ngrp = __gr_dup (grp);
168                 if (NULL == ngrp) {
169                         fprintf (stderr,
170                                  _("%s: Out of memory. Cannot update %s.\n"),
171                                  Prog, gr_dbname ());
172                         exit (13);      /* XXX */
173                 }
174                 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
175                 if (gr_update (ngrp) == 0) {
176                         fprintf (stderr,
177                                  _("%s: failed to prepare the new %s entry '%s'\n"),
178                                  Prog, gr_dbname (), ngrp->gr_name);
179                         exit (E_GRP_UPDATE);
180                 }
181
182                 /*
183                  * Update the DBM group file with the new entry as well.
184                  */
185 #ifdef WITH_AUDIT
186                 audit_logger (AUDIT_DEL_USER, Prog,
187                               "deleting user from group",
188                               user_name, (unsigned int) user_id,
189                               SHADOW_AUDIT_SUCCESS);
190 #endif
191                 SYSLOG ((LOG_INFO, "delete '%s' from group '%s'\n",
192                          user_name, ngrp->gr_name));
193         }
194
195         /*
196          * we've removed their name from all the groups above, so
197          * now if they have a group with the same name as their
198          * user name, with no members, we delete it.
199          * FIXME: below, the check for grp->gr_mem[0] is not sufficient.
200          *        We should retrieve the group with gr_locate and check
201          *        that gr_mem is empty.
202          */
203         grp = xgetgrnam (user_name);
204         if (   (NULL != grp)
205             && getdef_bool ("USERGROUPS_ENAB")
206             && (   (NULL == grp->gr_mem[0])
207                 || (   (NULL == grp->gr_mem[1])
208                     && (strcmp (grp->gr_mem[0], user_name) == 0)))) {
209
210                 pwd = NULL;
211                 if (!fflg) {
212                         /*
213                          * Scan the passwd file to check if this group is still
214                          * used as a primary group.
215                          */
216                         setpwent ();
217                         while ((pwd = getpwent ()) != NULL) {
218                                 if (strcmp (pwd->pw_name, user_name) == 0) {
219                                         continue;
220                                 }
221                                 if (pwd->pw_gid == grp->gr_gid) {
222                                         fprintf (stderr,
223                                                  _("%s: group %s is the primary group of another user and is not removed.\n"),
224                                                  Prog, grp->gr_name);
225                                         break;
226                                 }
227                         }
228                         endpwent ();
229                 }
230
231                 if (NULL == pwd) {
232                         /*
233                          * We can remove this group, it is not the primary
234                          * group of any remaining user.
235                          */
236                         if (gr_remove (grp->gr_name) == 0) {
237                                 fprintf (stderr,
238                                          _("%s: cannot remove entry '%s' from %s\n"),
239                                          Prog, grp->gr_name, gr_dbname ());
240                                 fail_exit (E_GRP_UPDATE);
241                         }
242
243 #ifdef SHADOWGRP
244                         deleted_user_group = true;
245 #endif
246
247 #ifdef WITH_AUDIT
248                         audit_logger (AUDIT_DEL_GROUP, Prog,
249                                       "deleting group",
250                                       grp->gr_name, AUDIT_NO_ID,
251                                       SHADOW_AUDIT_SUCCESS);
252 #endif
253                         SYSLOG ((LOG_INFO,
254                                  "removed group '%s' owned by '%s'\n",
255                                  grp->gr_name, user_name));
256                 }
257         }
258 #ifdef  SHADOWGRP
259         if (!is_shadow_grp) {
260                 return;
261         }
262
263         /*
264          * Scan through the entire shadow group file looking for the groups
265          * that the user is a member of. Both the administrative list and
266          * the ordinary membership list is checked.
267          */
268         for (sgr_rewind (), sgrp = sgr_next ();
269              NULL != sgrp;
270              sgrp = sgr_next ()) {
271                 bool was_member, was_admin;
272
273                 /*
274                  * See if the user specified this group as one of their
275                  * concurrent groups.
276                  */
277                 was_member = is_on_list (sgrp->sg_mem, user_name);
278                 was_admin = is_on_list (sgrp->sg_adm, user_name);
279
280                 if (!was_member && !was_admin) {
281                         continue;
282                 }
283
284                 nsgrp = __sgr_dup (sgrp);
285                 if (NULL == nsgrp) {
286                         fprintf (stderr,
287                                  _("%s: Out of memory. Cannot update %s.\n"),
288                                  Prog, sgr_dbname ());
289                         exit (13);      /* XXX */
290                 }
291
292                 if (was_member) {
293                         nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
294                 }
295
296                 if (was_admin) {
297                         nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
298                 }
299
300                 if (sgr_update (nsgrp) == 0) {
301                         fprintf (stderr,
302                                  _("%s: failed to prepare the new %s entry '%s'\n"),
303                                  Prog, sgr_dbname (), nsgrp->sg_name);
304                         exit (E_GRP_UPDATE);
305                 }
306 #ifdef WITH_AUDIT
307                 audit_logger (AUDIT_DEL_USER, Prog,
308                               "deleting user from shadow group",
309                               user_name, (unsigned int) user_id,
310                               SHADOW_AUDIT_SUCCESS);
311 #endif
312                 SYSLOG ((LOG_INFO, "delete '%s' from shadow group '%s'\n",
313                          user_name, nsgrp->sg_name));
314         }
315
316         if (   deleted_user_group
317             && (sgr_locate (user_name) != NULL)) {
318                 if (sgr_remove (user_name) == 0) {
319                         fprintf (stderr,
320                                  _("%s: cannot remove entry '%s' from %s\n"),
321                                  Prog, user_name, sgr_dbname ());
322                         fail_exit (E_GRP_UPDATE);
323                 }
324         }
325 #endif                          /* SHADOWGRP */
326 }
327
328 /*
329  * close_files - close all of the files that were opened
330  *
331  *      close_files() closes all of the files that were opened for this
332  *      new user. This causes any modified entries to be written out.
333  */
334 static void close_files (void)
335 {
336         if (pw_close () == 0) {
337                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
338                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
339                 fail_exit (E_PW_UPDATE);
340         }
341         if (pw_unlock () == 0) {
342                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
343                 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
344                 /* continue */
345         }
346         pw_locked = false;
347
348         if (is_shadow_pwd) {
349                 if (spw_close () == 0) {
350                         fprintf (stderr,
351                                  _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
352                         SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
353                         fail_exit (E_PW_UPDATE);
354                 }
355                 if (spw_unlock () == 0) {
356                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
357                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
358                         /* continue */
359                 }
360                 spw_locked = false;
361         }
362
363         if (gr_close () == 0) {
364                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
365                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
366                 fail_exit (E_GRP_UPDATE);
367         }
368         if (gr_unlock () == 0) {
369                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
370                 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
371                 /* continue */
372         }
373         gr_locked = false;
374
375 #ifdef  SHADOWGRP
376         if (is_shadow_grp) {
377                 if (sgr_close () == 0) {
378                         fprintf (stderr,
379                                  _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
380                         SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
381                         fail_exit (E_GRP_UPDATE);
382                 }
383
384                 if (sgr_unlock () == 0) {
385                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
386                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
387                         /* continue */
388                 }
389                 sgr_locked = false;
390         }
391 #endif
392 }
393
394 /*
395  * fail_exit - exit with a failure code after unlocking the files
396  */
397 static void fail_exit (int code)
398 {
399         if (pw_locked) {
400                 if (pw_unlock () == 0) {
401                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
402                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
403                         /* continue */
404                 }
405         }
406         if (gr_locked) {
407                 if (gr_unlock () == 0) {
408                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
409                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
410                         /* continue */
411                 }
412         }
413         if (spw_locked) {
414                 if (spw_unlock () == 0) {
415                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
416                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
417                         /* continue */
418                 }
419         }
420 #ifdef  SHADOWGRP
421         if (sgr_locked) {
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         }
428 #endif
429
430 #ifdef WITH_AUDIT
431         audit_logger (AUDIT_DEL_USER, Prog,
432                       "deleting user",
433                       user_name, (unsigned int) user_id,
434                       SHADOW_AUDIT_FAILURE);
435 #endif
436
437         exit (code);
438 }
439
440 /*
441  * open_files - lock and open the password files
442  *
443  *      open_files() opens the two password files.
444  */
445
446 static void open_files (void)
447 {
448         if (pw_lock () == 0) {
449                 fprintf (stderr,
450                          _("%s: cannot lock %s; try again later.\n"),
451                          Prog, pw_dbname ());
452 #ifdef WITH_AUDIT
453                 audit_logger (AUDIT_DEL_USER, Prog,
454                               "locking password file",
455                               user_name, (unsigned int) user_id,
456                               SHADOW_AUDIT_FAILURE);
457 #endif
458                 fail_exit (E_PW_UPDATE);
459         }
460         pw_locked = true;
461         if (pw_open (O_RDWR) == 0) {
462                 fprintf (stderr,
463                          _("%s: cannot open %s\n"), Prog, pw_dbname ());
464 #ifdef WITH_AUDIT
465                 audit_logger (AUDIT_DEL_USER, Prog,
466                               "opening password file",
467                               user_name, (unsigned int) user_id,
468                               SHADOW_AUDIT_FAILURE);
469 #endif
470                 fail_exit (E_PW_UPDATE);
471         }
472         if (is_shadow_pwd) {
473                 if (spw_lock () == 0) {
474                         fprintf (stderr,
475                                  _("%s: cannot lock %s; try again later.\n"),
476                                  Prog, spw_dbname ());
477 #ifdef WITH_AUDIT
478                         audit_logger (AUDIT_DEL_USER, Prog,
479                                       "locking shadow password file",
480                                       user_name, (unsigned int) user_id,
481                                       SHADOW_AUDIT_FAILURE);
482 #endif
483                         fail_exit (E_PW_UPDATE);
484                 }
485                 spw_locked = true;
486                 if (spw_open (O_RDWR) == 0) {
487                         fprintf (stderr,
488                                  _("%s: cannot open %s\n"),
489                                  Prog, spw_dbname ());
490 #ifdef WITH_AUDIT
491                         audit_logger (AUDIT_DEL_USER, Prog,
492                                       "opening shadow password file",
493                                       user_name, (unsigned int) user_id,
494                                       SHADOW_AUDIT_FAILURE);
495 #endif
496                         fail_exit (E_PW_UPDATE);
497                 }
498         }
499         if (gr_lock () == 0) {
500                 fprintf (stderr,
501                          _("%s: cannot lock %s; try again later.\n"),
502                          Prog, gr_dbname ());
503 #ifdef WITH_AUDIT
504                 audit_logger (AUDIT_DEL_USER, Prog,
505                               "locking group file",
506                               user_name, (unsigned int) user_id,
507                               SHADOW_AUDIT_FAILURE);
508 #endif
509                 fail_exit (E_GRP_UPDATE);
510         }
511         gr_locked = true;
512         if (gr_open (O_RDWR) == 0) {
513                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
514 #ifdef WITH_AUDIT
515                 audit_logger (AUDIT_DEL_USER, Prog,
516                               "opening group file",
517                               user_name, (unsigned int) user_id,
518                               SHADOW_AUDIT_FAILURE);
519 #endif
520                 fail_exit (E_GRP_UPDATE);
521         }
522 #ifdef  SHADOWGRP
523         if (is_shadow_grp) {
524                 if (sgr_lock () == 0) {
525                         fprintf (stderr,
526                                  _("%s: cannot lock %s; try again later.\n"),
527                                  Prog, sgr_dbname ());
528 #ifdef WITH_AUDIT
529                         audit_logger (AUDIT_DEL_USER, Prog,
530                                       "locking shadow group file",
531                                       user_name, (unsigned int) user_id,
532                                       SHADOW_AUDIT_FAILURE);
533 #endif
534                         fail_exit (E_GRP_UPDATE);
535                 }
536                 sgr_locked= true;
537                 if (sgr_open (O_RDWR) == 0) {
538                         fprintf (stderr, _("%s: cannot open %s\n"),
539                                  Prog, sgr_dbname ());
540 #ifdef WITH_AUDIT
541                         audit_logger (AUDIT_DEL_USER, Prog,
542                                       "opening shadow group file",
543                                       user_name, (unsigned int) user_id,
544                                       SHADOW_AUDIT_FAILURE);
545 #endif
546                         fail_exit (E_GRP_UPDATE);
547                 }
548         }
549 #endif
550 }
551
552 /*
553  * update_user - delete the user entries
554  *
555  *      update_user() deletes the password file entries for this user
556  *      and will update the group entries as required.
557  */
558 static void update_user (void)
559 {
560         if (pw_remove (user_name) == 0) {
561                 fprintf (stderr,
562                          _("%s: cannot remove entry '%s' from %s\n"),
563                          Prog, user_name, pw_dbname ());
564                 fail_exit (E_PW_UPDATE);
565         }
566         if (   is_shadow_pwd
567             && (spw_locate (user_name) != NULL)
568             && (spw_remove (user_name) == 0)) {
569                 fprintf (stderr,
570                          _("%s: cannot remove entry '%s' from %s\n"),
571                          Prog, user_name, spw_dbname ());
572                 fail_exit (E_PW_UPDATE);
573         }
574 #ifdef WITH_AUDIT
575         audit_logger (AUDIT_DEL_USER, Prog,
576                       "deleting user entries",
577                       user_name, (unsigned int) user_id,
578                       SHADOW_AUDIT_SUCCESS);
579 #endif
580         SYSLOG ((LOG_INFO, "delete user '%s'\n", user_name));
581 }
582
583 /* 
584  * user_cancel - cancel cron and at jobs
585  *
586  *      user_cancel calls a script for additional cleanups like removal of
587  *      cron, at, or print jobs.
588  */
589
590 static void user_cancel (const char *user)
591 {
592         char *cmd;
593         pid_t pid, wpid;
594         int status;
595
596         cmd = getdef_str ("USERDEL_CMD");
597         if (NULL == cmd) {
598                 return;
599         }
600         pid = fork ();
601         if (pid == 0) {
602                 execl (cmd, cmd, user, (char *) 0);
603                 perror (cmd);
604                 exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
605         } else if ((pid_t)-1 == pid) {
606                 perror ("fork");
607                 return;
608         }
609         do {
610                 wpid = wait (&status);
611         } while ((wpid != pid) && ((pid_t)-1 != wpid));
612 }
613
614 #ifdef EXTRA_CHECK_HOME_DIR
615 static bool path_prefix (const char *s1, const char *s2)
616 {
617         return (   (strncmp (s2, s1, strlen (s1)) == 0)
618                 && (   ('\0' == s2[strlen (s1)])
619                     || ('/'  == s2[strlen (s1)])));
620 }
621 #endif
622
623 /*
624  * is_owner - Check if path is owned by uid
625  *
626  * Return
627  *  1: path exists and is owned by uid
628  *  0: path is not owned by uid, or a failure occured
629  * -1: path does not exist
630  */
631 static int is_owner (uid_t uid, const char *path)
632 {
633         struct stat st;
634
635         errno = 0;
636         if (stat (path, &st) != 0) {
637                 if ((ENOENT == errno) || (ENOTDIR == errno)) {
638                         /* The file or directory does not exist */
639                         return -1;
640                 } else {
641                         return 0;
642                 }
643         }
644         return (st.st_uid == uid);
645 }
646
647 static int remove_mailbox (void)
648 {
649         const char *maildir;
650         char mailfile[1024];
651         int i;
652         int errors = 0;
653
654         maildir = getdef_str ("MAIL_DIR");
655 #ifdef MAIL_SPOOL_DIR
656         if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
657                 maildir = MAIL_SPOOL_DIR;
658         }
659 #endif
660         if (NULL == maildir) {
661                 return 0;
662         }
663         snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
664         if (fflg) {
665                 if (unlink (mailfile) != 0) {
666                         fprintf (stderr,
667                                  _("%s: warning: can't remove %s: %s\n"),
668                                  Prog, mailfile, strerror (errno));
669                         SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
670 #ifdef WITH_AUDIT
671                         audit_logger (AUDIT_DEL_USER, Prog,
672                                       "deleting mail file",
673                                       user_name, (unsigned int) user_id,
674                                       SHADOW_AUDIT_FAILURE);
675 #endif
676                         errors = 1;
677                         /* continue */
678                 }
679 #ifdef WITH_AUDIT
680                 else
681                 {
682                         audit_logger (AUDIT_DEL_USER, Prog,
683                                       "deleting mail file",
684                                       user_name, (unsigned int) user_id,
685                                       SHADOW_AUDIT_SUCCESS);
686                 }
687 #endif
688                 return errors;
689         }
690         i = is_owner (user_id, mailfile);
691         if (i == 0) {
692                 fprintf (stderr,
693                          _("%s: %s not owned by %s, not removing\n"),
694                          Prog, mailfile, user_name);
695                 SYSLOG ((LOG_ERR,
696                          "%s not owned by %s, not removed",
697                          mailfile, strerror (errno)));
698 #ifdef WITH_AUDIT
699                 audit_logger (AUDIT_DEL_USER, Prog,
700                               "deleting mail file",
701                               user_name, (unsigned int) user_id,
702                               SHADOW_AUDIT_FAILURE);
703 #endif
704                 return 1;
705         } else if (i == -1) {
706                 return 0;               /* mailbox doesn't exist */
707         }
708         if (unlink (mailfile) != 0) {
709                 fprintf (stderr,
710                          _("%s: warning: can't remove %s: %s\n"),
711                          Prog, mailfile, strerror (errno));
712                 SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
713 #ifdef WITH_AUDIT
714                 audit_logger (AUDIT_DEL_USER, Prog,
715                               "deleting mail file",
716                               user_name, (unsigned int) user_id,
717                               SHADOW_AUDIT_FAILURE);
718 #endif
719                 errors = 1;
720                 /* continue */
721         }
722 #ifdef WITH_AUDIT
723         else
724         {
725                 audit_logger (AUDIT_DEL_USER, Prog,
726                               "deleting mail file",
727                               user_name, (unsigned int) user_id,
728                               SHADOW_AUDIT_SUCCESS);
729         }
730 #endif
731         return errors;
732 }
733
734 /*
735  * main - userdel command
736  */
737 int main (int argc, char **argv)
738 {
739         int errors = 0; /* Error in the removal of the home directory */
740
741 #ifdef ACCT_TOOLS_SETUID
742 #ifdef USE_PAM
743         pam_handle_t *pamh = NULL;
744         int retval;
745 #endif                          /* USE_PAM */
746 #endif                          /* ACCT_TOOLS_SETUID */
747
748 #ifdef WITH_AUDIT
749         audit_help_open ();
750 #endif
751
752         /*
753          * Get my name so that I can use it to report errors.
754          */
755         Prog = Basename (argv[0]);
756         (void) setlocale (LC_ALL, "");
757         (void) bindtextdomain (PACKAGE, LOCALEDIR);
758         (void) textdomain (PACKAGE);
759
760         {
761                 /*
762                  * Parse the command line options.
763                  */
764                 int c;
765                 static struct option long_options[] = {
766                         {"force", no_argument, NULL, 'f'},
767                         {"help", no_argument, NULL, 'h'},
768                         {"remove", no_argument, NULL, 'r'},
769                         {NULL, 0, NULL, '\0'}
770                 };
771                 while ((c = getopt_long (argc, argv, "fhr",
772                                          long_options, NULL)) != -1) {
773                         switch (c) {
774                         case 'f':       /* force remove even if not owned by user */
775                                 fflg = true;
776                                 break;
777                         case 'r':       /* remove home dir and mailbox */
778                                 rflg = true;
779                                 break;
780                         default:
781                                 usage ();
782                         }
783                 }
784         }
785
786         if ((optind + 1) != argc) {
787                 usage ();
788         }
789
790         OPENLOG ("userdel");
791
792 #ifdef ACCT_TOOLS_SETUID
793 #ifdef USE_PAM
794         {
795                 struct passwd *pampw;
796                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
797                 if (pampw == NULL) {
798                         fprintf (stderr,
799                                  _("%s: Cannot determine your user name.\n"),
800                                  Prog);
801                         exit (E_PW_UPDATE);
802                 }
803
804                 retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh);
805         }
806
807         if (PAM_SUCCESS == retval) {
808                 retval = pam_authenticate (pamh, 0);
809         }
810
811         if (PAM_SUCCESS == retval) {
812                 retval = pam_acct_mgmt (pamh, 0);
813         }
814
815         if (NULL != pamh) {
816                 (void) pam_end (pamh, retval);
817         }
818         if (PAM_SUCCESS != retval) {
819                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
820                 exit (E_PW_UPDATE);
821         }
822 #endif                          /* USE_PAM */
823 #endif                          /* ACCT_TOOLS_SETUID */
824
825         is_shadow_pwd = spw_file_present ();
826 #ifdef SHADOWGRP
827         is_shadow_grp = sgr_file_present ();
828 #endif
829
830         /*
831          * Start with a quick check to see if the user exists.
832          */
833         user_name = argv[argc - 1];
834         {
835                 struct passwd *pwd;
836                 pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
837                 if (NULL == pwd) {
838                         fprintf (stderr, _("%s: user '%s' does not exist\n"),
839                                  Prog, user_name);
840 #ifdef WITH_AUDIT
841                         audit_logger (AUDIT_DEL_USER, Prog,
842                                       "deleting user not found",
843                                       user_name, AUDIT_NO_ID,
844                                       SHADOW_AUDIT_FAILURE);
845 #endif
846                         exit (E_NOTFOUND);
847                 }
848                 user_id = pwd->pw_uid;
849                 user_home = xstrdup (pwd->pw_dir);
850         }
851 #ifdef  USE_NIS
852
853         /*
854          * Now make sure it isn't an NIS user.
855          */
856         if (__ispwNIS ()) {
857                 char *nis_domain;
858                 char *nis_master;
859
860                 fprintf (stderr,
861                          _("%s: user %s is a NIS user\n"), Prog, user_name);
862                 if (   !yp_get_default_domain (&nis_domain)
863                     && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
864                         fprintf (stderr,
865                                  _("%s: %s is the NIS master\n"),
866                                  Prog, nis_master);
867                 }
868                 exit (E_NOTFOUND);
869         }
870 #endif
871         /*
872          * Check to make certain the user isn't logged in.
873          * Note: This is a best effort basis. The user may log in between,
874          * a cron job may be started on her behalf, etc.
875          */
876         if (user_busy (user_name, user_id) != 0) {
877                 fprintf (stderr,
878                          _("%s: user %s is currently logged in\n"),
879                          Prog, user_name);
880                 if (!fflg) {
881 #ifdef WITH_AUDIT
882                         audit_logger (AUDIT_DEL_USER, Prog,
883                                       "deleting user logged in",
884                                       user_name, AUDIT_NO_ID,
885                                       SHADOW_AUDIT_FAILURE);
886 #endif
887                         exit (E_USER_BUSY);
888                 }
889         }
890
891         /*
892          * Do the hard stuff - open the files, create the user entries,
893          * create the home directory, then close and update the files.
894          */
895         open_files ();
896         update_user ();
897         update_groups ();
898
899         if (rflg) {
900                 errors += remove_mailbox ();
901         }
902         if (rflg) {
903                 int home_owned = is_owner (user_id, user_home);
904                 if (-1 == home_owned) {
905                         fprintf (stderr,
906                                  _("%s: %s home directory (%s) not found\n"),
907                                  Prog, user_name, user_home);
908                         rflg = 0;
909                 } else if ((0 == home_owned) && !fflg) {
910                         fprintf (stderr,
911                                  _("%s: %s not owned by %s, not removing\n"),
912                                  Prog, user_home, user_name);
913                         rflg = 0;
914                         errors++;
915                         /* continue */
916                 }
917         }
918
919 #ifdef EXTRA_CHECK_HOME_DIR
920         /* This may be slow, the above should be good enough. */
921         if (rflg && !fflg) {
922                 struct passwd *pwd;
923                 /*
924                  * For safety, refuse to remove the home directory if it
925                  * would result in removing some other user's home
926                  * directory. Still not perfect so be careful, but should
927                  * prevent accidents if someone has /home or / as home
928                  * directory...  --marekm
929                  */
930                 setpwent ();
931                 while ((pwd = getpwent ())) {
932                         if (strcmp (pwd->pw_name, user_name) == 0) {
933                                 continue;
934                         }
935                         if (path_prefix (user_home, pwd->pw_dir)) {
936                                 fprintf (stderr,
937                                          _
938                                          ("%s: not removing directory %s (would remove home of user %s)\n"),
939                                          Prog, user_home, pwd->pw_name);
940                                 rflg = false;
941                                 errors++;
942                                 /* continue */
943                                 break;
944                         }
945                 }
946                 endpwent ();
947         }
948 #endif
949
950         if (rflg) {
951                 if (remove_tree (user_home) != 0) {
952                         fprintf (stderr,
953                                  _("%s: error removing directory %s\n"),
954                                  Prog, user_home);
955                         errors++;
956                         /* continue */
957                 }
958 #ifdef WITH_AUDIT
959                 else
960                 {
961                         audit_logger (AUDIT_DEL_USER, Prog,
962                                       "deleting home directory",
963                                       user_name, (unsigned int) user_id,
964                                       SHADOW_AUDIT_SUCCESS);
965                 }
966 #endif
967         }
968 #ifdef WITH_AUDIT
969         if (0 != errors) {
970                 audit_logger (AUDIT_DEL_USER, Prog,
971                               "deleting home directory",
972                               user_name, AUDIT_NO_ID,
973                               SHADOW_AUDIT_FAILURE);
974         }
975 #endif
976
977 #ifdef WITH_SELINUX
978         if (is_selinux_enabled () > 0) {
979                 const char *args[5];
980                 args[0] = "/usr/sbin/semanage";
981                 args[1] = "login";
982                 args[2] = "-d";
983                 args[3] = user_name;
984                 args[4] = NULL;
985                 safe_system (args[0], args, NULL, 1);
986         }
987 #endif
988
989         /*
990          * Cancel any crontabs or at jobs. Have to do this before we remove
991          * the entry from /etc/passwd.
992          */
993         user_cancel (user_name);
994         close_files ();
995
996         nscd_flush_cache ("passwd");
997         nscd_flush_cache ("group");
998
999         return ((0 != errors) ? E_HOMEDIR : E_SUCCESS);
1000 }
1001