tftpd: new applet (mostly using existing code for tftp)
[oweals/busybox.git] / loginutils / adduser.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * adduser - add users to /etc/passwd and /etc/shadow
4  *
5  * Copyright (C) 1999 by Lineo, inc. and John Beppu
6  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7  *
8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9  */
10
11 #include "libbb.h"
12
13 #define OPT_DONT_SET_PASS  (1 << 4)
14 #define OPT_SYSTEM_ACCOUNT (1 << 5)
15 #define OPT_DONT_MAKE_HOME (1 << 6)
16
17
18 /* remix */
19 /* recoded such that the uid may be passed in *p */
20 static void passwd_study(struct passwd *p)
21 {
22         int max;
23
24         if (getpwnam(p->pw_name))
25                 bb_error_msg_and_die("login '%s' is in use", p->pw_name);
26
27         if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
28                 p->pw_uid = 0;
29                 max = 999;
30         } else {
31                 p->pw_uid = 1000;
32                 max = 64999;
33         }
34
35         /* check for a free uid (and maybe gid) */
36         while (getpwuid(p->pw_uid) || (!p->pw_gid && getgrgid(p->pw_uid)))
37                 p->pw_uid++;
38
39         if (!p->pw_gid) {
40                 /* new gid = uid */
41                 p->pw_gid = p->pw_uid;
42                 if (getgrnam(p->pw_name))
43                         bb_error_msg_and_die("group name '%s' is in use", p->pw_name);
44         }
45
46         if (p->pw_uid > max)
47                 bb_error_msg_and_die("no free uids left");
48 }
49
50 static void addgroup_wrapper(struct passwd *p)
51 {
52         char *cmd;
53
54         cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
55         system(cmd);
56         free(cmd);
57 }
58
59 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
60
61 static void passwd_wrapper(const char *login)
62 {
63         static const char prog[] ALIGN1 = "passwd";
64
65         BB_EXECLP(prog, prog, login, NULL);
66         bb_error_msg_and_die("cannot execute %s, you must set password manually", prog);
67 }
68
69 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
70 static const char adduser_longopts[] ALIGN1 =
71                 "home\0"                Required_argument "h"
72                 "gecos\0"               Required_argument "g"
73                 "shell\0"               Required_argument "s"
74                 "ingroup\0"             Required_argument "G"
75                 "disabled-password\0"   No_argument       "D"
76                 "empty-password\0"      No_argument       "D"
77                 "system\0"              No_argument       "S"
78                 "no-create-home\0"      No_argument       "H"
79                 ;
80 #endif
81
82 /*
83  * adduser will take a login_name as its first parameter.
84  * home, shell, gecos:
85  * can be customized via command-line parameters.
86  */
87 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
88 int adduser_main(int argc ATTRIBUTE_UNUSED, char **argv)
89 {
90         struct passwd pw;
91         const char *usegroup = NULL;
92         FILE *file;
93
94 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
95         applet_long_options = adduser_longopts;
96 #endif
97
98         /* got root? */
99         if (geteuid()) {
100                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
101         }
102
103         pw.pw_gecos = (char *)"Linux User,,,";
104         pw.pw_shell = (char *)DEFAULT_SHELL;
105         pw.pw_dir = NULL;
106
107         /* exactly one non-option arg */
108         opt_complementary = "=1";
109         getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
110         argv += optind;
111
112         /* fill in the passwd struct */
113         pw.pw_name = argv[0];
114         if (!pw.pw_dir) {
115                 /* create string for $HOME if not specified already */
116                 pw.pw_dir = xasprintf("/home/%s", argv[0]);
117         }
118         pw.pw_passwd = (char *)"x";
119         pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
120
121         /* make sure everything is kosher and setup uid && maybe gid */
122         passwd_study(&pw);
123
124         /* add to passwd */
125         file = xfopen(bb_path_passwd_file, "a");
126         //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */
127         if (putpwent(&pw, file) != 0) {
128                 bb_perror_nomsg_and_die();
129         }
130         /* do fclose even if !ENABLE_FEATURE_CLEAN_UP.
131          * We will exec passwd, files must be flushed & closed before that! */
132         fclose(file);
133
134 #if ENABLE_FEATURE_SHADOWPASSWDS
135         /* add to shadow if necessary */
136         file = fopen_or_warn(bb_path_shadow_file, "a");
137         if (file) {
138                 //fseek(file, 0, SEEK_END);
139                 fprintf(file, "%s:!:%u:0:99999:7:::\n",
140                                 pw.pw_name,             /* username */
141                                 (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */
142                                 /*0,*/                  /* sp->sp_min */
143                                 /*99999,*/              /* sp->sp_max */
144                                 /*7*/                   /* sp->sp_warn */
145                 );
146                 fclose(file);
147         }
148 #endif
149
150         /* add to group */
151         /* addgroup should be responsible for dealing w/ gshadow */
152         /* if using a pre-existing group, don't create one */
153         if (!usegroup)
154                 addgroup_wrapper(&pw);
155
156         /* Clear the umask for this process so it doesn't
157          * screw up the permissions on the mkdir and chown. */
158         umask(0);
159         if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
160                 /* Set the owner and group so it is owned by the new user,
161                    then fix up the permissions to 2755. Can't do it before
162                    since chown will clear the setgid bit */
163                 if (mkdir(pw.pw_dir, 0755)
164                  || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid)
165                  || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */
166                 ) {
167                         bb_simple_perror_msg(pw.pw_dir);
168                 }
169         }
170
171         if (!(option_mask32 & OPT_DONT_SET_PASS)) {
172                 /* interactively set passwd */
173                 passwd_wrapper(pw.pw_name);
174         }
175
176         return 0;
177 }