e91417ad55cf795cb5d328806bea90a7cd5fc4c4
[platform/upstream/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 /*
70  * adduser will take a login_name as its first parameter.
71  * home, shell, gecos:
72  * can be customized via command-line parameters.
73  */
74 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
75 int adduser_main(int argc, char **argv)
76 {
77         struct passwd pw;
78         const char *usegroup = NULL;
79         FILE *file;
80
81         /* got root? */
82         if (geteuid()) {
83                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
84         }
85
86         pw.pw_gecos = (char *)"Linux User,,,";
87         pw.pw_shell = (char *)DEFAULT_SHELL;
88         pw.pw_dir = NULL;
89
90         /* exactly one non-option arg */
91         opt_complementary = "=1";
92         getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
93         argv += optind;
94
95         /* fill in the passwd struct */
96         pw.pw_name = argv[0];
97         if (!pw.pw_dir) {
98                 /* create string for $HOME if not specified already */
99                 pw.pw_dir = xasprintf("/home/%s", argv[0]);
100         }
101         pw.pw_passwd = (char *)"x";
102         pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
103
104         /* make sure everything is kosher and setup uid && maybe gid */
105         passwd_study(&pw);
106
107         /* add to passwd */
108         file = xfopen(bb_path_passwd_file, "a");
109         //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */
110         if (putpwent(&pw, file) != 0) {
111                 bb_perror_nomsg_and_die();
112         }
113         /* do fclose even if !ENABLE_FEATURE_CLEAN_UP.
114          * We will exec passwd, files must be flushed & closed before that! */
115         fclose(file);
116
117 #if ENABLE_FEATURE_SHADOWPASSWDS
118         /* add to shadow if necessary */
119         file = fopen_or_warn(bb_path_shadow_file, "a");
120         if (file) {
121                 //fseek(file, 0, SEEK_END);
122                 fprintf(file, "%s:!:%u:0:99999:7:::\n",
123                                 pw.pw_name,             /* username */
124                                 (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */
125                                 /*0,*/                  /* sp->sp_min */
126                                 /*99999,*/              /* sp->sp_max */
127                                 /*7*/                   /* sp->sp_warn */
128                 );
129                 fclose(file);
130         }
131 #endif
132
133         /* add to group */
134         /* addgroup should be responsible for dealing w/ gshadow */
135         /* if using a pre-existing group, don't create one */
136         if (!usegroup)
137                 addgroup_wrapper(&pw);
138
139         /* Clear the umask for this process so it doesn't
140          * screw up the permissions on the mkdir and chown. */
141         umask(0);
142         if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
143                 /* Set the owner and group so it is owned by the new user,
144                    then fix up the permissions to 2755. Can't do it before
145                    since chown will clear the setgid bit */
146                 if (mkdir(pw.pw_dir, 0755)
147                  || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid)
148                  || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */
149                 ) { 
150                         bb_simple_perror_msg(pw.pw_dir);
151                 }
152         }
153
154         if (!(option_mask32 & OPT_DONT_SET_PASS)) {
155                 /* interactively set passwd */
156                 passwd_wrapper(pw.pw_name);
157         }
158
159         return 0;
160 }