sulogin: add support for $SUSHELL & $sushell
[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 "busybox.h"
12
13 #define DONT_SET_PASS                   (1 << 4)
14 #define DONT_MAKE_HOME                  (1 << 6)
15
16
17 /* remix */
18 /* EDR recoded such that the uid may be passed in *p */
19 static int passwd_study(const char *filename, struct passwd *p)
20 {
21         struct passwd *pw;
22         FILE *passwd;
23
24         const int min = 500;
25         const int max = 65000;
26
27         passwd = xfopen(filename, "r");
28
29         /* EDR if uid is out of bounds, set to min */
30         if ((p->pw_uid > max) || (p->pw_uid < min))
31                 p->pw_uid = min;
32
33         /* stuff to do:
34          * make sure login isn't taken;
35          * find free uid and gid;
36          */
37         while ((pw = fgetpwent(passwd))) {
38                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
39                         /* return 0; */
40                         return 1;
41                 }
42                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
43                         && (pw->pw_uid >= min)) {
44                         p->pw_uid = pw->pw_uid + 1;
45                 }
46         }
47
48         if (p->pw_gid == 0) {
49                 /* EDR check for an already existing gid */
50                 while (getgrgid(p->pw_uid) != NULL)
51                         p->pw_uid++;
52
53                 /* EDR also check for an existing group definition */
54                 if (getgrnam(p->pw_name) != NULL)
55                         return 3;
56
57                 /* EDR create new gid always = uid */
58                 p->pw_gid = p->pw_uid;
59         }
60
61         /* EDR bounds check */
62         if ((p->pw_uid > max) || (p->pw_uid < min))
63                 return 2;
64
65         /* return 1; */
66         return 0;
67 }
68
69 static void addgroup_wrapper(struct passwd *p)
70 {
71         char *cmd;
72
73         cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name);
74         system(cmd);
75         free(cmd);
76 }
77
78 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
79
80 static void passwd_wrapper(const char *login)
81 {
82         static const char prog[] = "passwd";
83         execlp(prog, prog, login, NULL);
84         bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
85 }
86
87 /* putpwent(3) remix */
88 static int adduser(struct passwd *p, unsigned long flags)
89 {
90         FILE *file;
91         int addgroup = !p->pw_gid;
92
93         /* make sure everything is kosher and setup uid && gid */
94         file = xfopen(bb_path_passwd_file, "a");
95         fseek(file, 0, SEEK_END);
96
97         switch (passwd_study(bb_path_passwd_file, p)) {
98                 case 1:
99                         bb_error_msg_and_die("%s: login already in use", p->pw_name);
100                 case 2:
101                         bb_error_msg_and_die("illegal uid or no uids left");
102                 case 3:
103                         bb_error_msg_and_die("%s: group name already in use", p->pw_name);
104         }
105
106         /* add to passwd */
107         if (putpwent(p, file) == -1) {
108                 bb_perror_nomsg_and_die();
109         }
110         fclose(file);
111
112 #if ENABLE_FEATURE_SHADOWPASSWDS
113         /* add to shadow if necessary */
114         file = xfopen(bb_path_shadow_file, "a");
115         fseek(file, 0, SEEK_END);
116         fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
117                         p->pw_name,             /* username */
118                         time(NULL) / 86400,     /* sp->sp_lstchg */ 
119                         0,                      /* sp->sp_min */
120                         99999,                  /* sp->sp_max */
121                         7);                     /* sp->sp_warn */
122         fclose(file);
123 #endif
124
125         /* add to group */
126         /* addgroup should be responsible for dealing w/ gshadow */
127         /* if using a pre-existing group, don't create one */
128         if (addgroup) addgroup_wrapper(p);
129
130         /* Clear the umask for this process so it doesn't
131          * * screw up the permissions on the mkdir and chown. */
132         umask(0);
133         if (!(flags & DONT_MAKE_HOME)) {
134                 /* Set the owner and group so it is owned by the new user,
135                    then fix up the permissions to 2755. Can't do it before
136                    since chown will clear the setgid bit */
137                 if (mkdir(p->pw_dir, 0755)
138                 || chown(p->pw_dir, p->pw_uid, p->pw_gid)
139                 || chmod(p->pw_dir, 02755)) {
140                         bb_perror_msg("%s", p->pw_dir);
141                 }
142         }
143
144         if (!(flags & DONT_SET_PASS)) {
145                 /* interactively set passwd */
146                 passwd_wrapper(p->pw_name);
147         }
148
149         return 0;
150 }
151
152 /*
153  * adduser will take a login_name as its first parameter.
154  *
155  * home
156  * shell
157  * gecos
158  *
159  * can be customized via command-line parameters.
160  * ________________________________________________________________________ */
161 int adduser_main(int argc, char **argv)
162 {
163         struct passwd pw;
164         const char *usegroup = NULL;
165         unsigned long flags;
166
167         pw.pw_gecos = "Linux User,,,";
168         pw.pw_shell = (char *)DEFAULT_SHELL;
169         pw.pw_dir = NULL;
170
171         /* check for min, max and missing args and exit on error */
172         opt_complementary = "-1:?1:?";
173         flags = getopt32(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
174
175         /* got root? */
176         if(geteuid()) {
177                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
178         }
179
180         /* create string for $HOME if not specified already */
181         if (!pw.pw_dir) {
182                 snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
183                 pw.pw_dir =  &bb_common_bufsiz1[0];
184         }
185
186         /* create a passwd struct */
187         pw.pw_name = argv[optind];
188         pw.pw_passwd = "x";
189         pw.pw_uid = 0;
190         pw.pw_gid = (usegroup) ? bb_xgetgrnam(usegroup) : 0; /* exits on failure */
191
192         /* grand finale */
193         return adduser(&pw, flags);
194 }