Line data Source code
1 : /*
2 : SSSD
3 :
4 : sss_useradd
5 :
6 : Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
7 : Copyright (C) Simo Sorce <ssorce@redhat.com> 2009
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <talloc.h>
26 : #include <popt.h>
27 : #include <errno.h>
28 : #include <unistd.h>
29 :
30 : #include "util/util.h"
31 : #include "db/sysdb.h"
32 : #include "tools/tools_util.h"
33 : #include "tools/sss_sync_ops.h"
34 :
35 0 : int main(int argc, const char **argv)
36 : {
37 0 : uid_t pc_uid = 0;
38 0 : const char *pc_gecos = NULL;
39 0 : const char *pc_home = NULL;
40 0 : char *pc_shell = NULL;
41 0 : int pc_debug = SSSDBG_DEFAULT;
42 0 : int pc_create_home = 0;
43 0 : const char *pc_username = NULL;
44 0 : const char *pc_skeldir = NULL;
45 0 : const char *pc_selinux_user = NULL;
46 0 : struct poptOption long_options[] = {
47 : POPT_AUTOHELP
48 0 : { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0, _("The debug level to run with"), NULL },
49 0 : { "uid", 'u', POPT_ARG_INT, &pc_uid, 0, _("The UID of the user"), NULL },
50 0 : { "gecos", 'c', POPT_ARG_STRING, &pc_gecos, 0, _("The comment string"), NULL },
51 0 : { "home", 'h', POPT_ARG_STRING, &pc_home, 0, _("Home directory"), NULL },
52 0 : { "shell", 's', POPT_ARG_STRING, &pc_shell, 0, _("Login shell"), NULL },
53 0 : { "groups", 'G', POPT_ARG_STRING, NULL, 'G', _("Groups"), NULL },
54 0 : { "create-home", 'm', POPT_ARG_NONE, NULL, 'm', _("Create user's directory if it does not exist"), NULL },
55 0 : { "no-create-home", 'M', POPT_ARG_NONE, NULL, 'M', _("Never create user's directory, overrides config"), NULL },
56 0 : { "skel", 'k', POPT_ARG_STRING, &pc_skeldir, 0, _("Specify an alternative skeleton directory"), NULL },
57 0 : { "selinux-user", 'Z', POPT_ARG_STRING, &pc_selinux_user, 0, _("The SELinux user for user's login"), NULL },
58 : POPT_TABLEEND
59 : };
60 0 : poptContext pc = NULL;
61 0 : struct tools_ctx *tctx = NULL;
62 0 : char *groups = NULL;
63 0 : char *badgroup = NULL;
64 : int ret;
65 : errno_t sret;
66 0 : bool in_transaction = false;
67 :
68 0 : debug_prg_name = argv[0];
69 :
70 0 : ret = set_locale();
71 0 : if (ret != EOK) {
72 0 : DEBUG(SSSDBG_CRIT_FAILURE,
73 : "set_locale failed (%d): %s\n", ret, strerror(ret));
74 0 : ERROR("Error setting the locale\n");
75 0 : ret = EXIT_FAILURE;
76 0 : goto fini;
77 : }
78 :
79 : /* parse parameters */
80 0 : pc = poptGetContext(NULL, argc, argv, long_options, 0);
81 0 : poptSetOtherOptionHelp(pc, "USERNAME");
82 0 : while ((ret = poptGetNextOpt(pc)) > 0) {
83 0 : switch (ret) {
84 : case 'G':
85 0 : groups = poptGetOptArg(pc);
86 0 : if (!groups) {
87 0 : BAD_POPT_PARAMS(pc, _("Specify group to add to\n"),
88 : ret, fini);
89 : }
90 0 : break;
91 :
92 : case 'm':
93 0 : pc_create_home = DO_CREATE_HOME;
94 0 : break;
95 :
96 : case 'M':
97 0 : pc_create_home = DO_NOT_CREATE_HOME;
98 0 : break;
99 : }
100 : }
101 :
102 0 : DEBUG_CLI_INIT(pc_debug);
103 :
104 0 : if (ret != -1) {
105 0 : BAD_POPT_PARAMS(pc, poptStrerror(ret), ret, fini);
106 : }
107 :
108 : /* username is an argument without --option */
109 0 : pc_username = poptGetArg(pc);
110 0 : if (pc_username == NULL) {
111 0 : BAD_POPT_PARAMS(pc, _("Specify user to add\n"), ret, fini);
112 : }
113 :
114 0 : CHECK_ROOT(ret, debug_prg_name);
115 :
116 0 : ret = init_sss_tools(&tctx);
117 0 : if (ret != EOK) {
118 0 : DEBUG(SSSDBG_CRIT_FAILURE,
119 : "init_sss_tools failed (%d): %s\n", ret, strerror(ret));
120 0 : if (ret == ENOENT) {
121 0 : ERROR("Error initializing the tools - no local domain\n");
122 : } else {
123 0 : ERROR("Error initializing the tools\n");
124 : }
125 0 : ret = EXIT_FAILURE;
126 0 : goto fini;
127 : }
128 :
129 : /* if the domain was not given as part of FQDN, default to local domain */
130 0 : ret = parse_name_domain(tctx, pc_username);
131 0 : if (ret != EOK) {
132 0 : ERROR("Invalid domain specified in FQDN\n");
133 0 : ret = EXIT_FAILURE;
134 0 : goto fini;
135 : }
136 :
137 0 : if (groups) {
138 0 : ret = parse_groups(tctx, groups, &tctx->octx->addgroups);
139 0 : if (ret != EOK) {
140 0 : DEBUG(SSSDBG_CRIT_FAILURE,
141 : "Cannot parse groups to add the user to\n");
142 0 : ERROR("Internal error while parsing parameters\n");
143 0 : ret = EXIT_FAILURE;
144 0 : goto fini;
145 : }
146 :
147 0 : ret = parse_group_name_domain(tctx, tctx->octx->addgroups);
148 0 : if (ret != EOK) {
149 0 : DEBUG(SSSDBG_CRIT_FAILURE,
150 : "Cannot parse FQDN groups to add the user to\n");
151 0 : ERROR("Groups must be in the same domain as user\n");
152 0 : ret = EXIT_FAILURE;
153 0 : goto fini;
154 : }
155 :
156 : /* Check group names in the LOCAL domain */
157 0 : ret = check_group_names(tctx, tctx->octx->addgroups, &badgroup);
158 0 : if (ret != EOK) {
159 0 : ERROR("Cannot find group %1$s in local domain\n", badgroup);
160 0 : ret = EXIT_FAILURE;
161 0 : goto fini;
162 : }
163 : }
164 :
165 0 : tctx->octx->uid = pc_uid;
166 :
167 : /*
168 : * Fills in defaults for ops_ctx user did not specify.
169 : */
170 0 : ret = useradd_defaults(tctx, tctx->confdb, tctx->octx,
171 : pc_gecos, pc_home, pc_shell,
172 : pc_create_home, pc_skeldir);
173 0 : if (ret != EOK) {
174 0 : ERROR("Cannot set default values\n");
175 0 : ret = EXIT_FAILURE;
176 0 : goto fini;
177 : }
178 :
179 : /* arguments processed, go on to actual work */
180 0 : if (id_in_range(tctx->octx->uid, tctx->octx->domain) != EOK) {
181 0 : ERROR("The selected UID is outside the allowed range\n");
182 0 : ret = EXIT_FAILURE;
183 0 : goto fini;
184 : }
185 :
186 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
187 0 : if (tctx->error != EOK) {
188 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
189 0 : goto done;
190 : }
191 0 : in_transaction = true;
192 :
193 : /* useradd */
194 0 : tctx->error = useradd(tctx, tctx->octx);
195 0 : if (tctx->error) {
196 0 : goto done;
197 : }
198 :
199 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
200 0 : if (tctx->error) {
201 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
202 0 : goto done;
203 : }
204 0 : in_transaction = false;
205 :
206 : /* Set SELinux login context - must be done after transaction is done
207 : * b/c libselinux calls getpwnam */
208 0 : ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
209 0 : if (ret != EOK) {
210 0 : ERROR("Cannot set SELinux login context\n");
211 0 : ret = EXIT_FAILURE;
212 0 : goto fini;
213 : }
214 :
215 : /* Create user's home directory and/or mail spool */
216 0 : if (tctx->octx->create_homedir) {
217 : /* We need to know the UID of the user, if
218 : * sysdb did assign it automatically, do a lookup */
219 0 : if (tctx->octx->uid == 0) {
220 0 : ret = sysdb_getpwnam_sync(tctx,
221 0 : tctx->octx->name,
222 0 : tctx->octx);
223 0 : if (ret != EOK) {
224 0 : ERROR("Cannot get info about the user\n");
225 0 : ret = EXIT_FAILURE;
226 0 : goto fini;
227 : }
228 : }
229 :
230 0 : ret = create_homedir(tctx->octx->skeldir,
231 0 : tctx->octx->home,
232 0 : tctx->octx->uid,
233 0 : tctx->octx->gid,
234 0 : tctx->octx->umask);
235 0 : if (ret == EEXIST) {
236 0 : ERROR("User's home directory already exists, not copying "
237 : "data from skeldir\n");
238 0 : } else if (ret != EOK) {
239 0 : ERROR("Cannot create user's home directory: %1$s\n", strerror(ret));
240 0 : ret = EXIT_FAILURE;
241 0 : goto fini;
242 : }
243 :
244 0 : ret = create_mail_spool(tctx,
245 0 : tctx->octx->name,
246 0 : tctx->octx->maildir,
247 0 : tctx->octx->uid,
248 0 : tctx->octx->gid);
249 0 : if (ret != EOK) {
250 0 : ERROR("Cannot create user's mail spool: %1$s\n", strerror(ret));
251 0 : DEBUG(SSSDBG_CRIT_FAILURE,
252 : "Cannot create user's mail spool: [%d][%s].\n",
253 : ret, strerror(ret));
254 0 : ret = EXIT_FAILURE;
255 0 : goto fini;
256 : }
257 : }
258 :
259 : done:
260 0 : if (in_transaction) {
261 0 : sret = sysdb_transaction_cancel(tctx->sysdb);
262 0 : if (sret != EOK) {
263 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
264 : }
265 : }
266 :
267 0 : if (tctx->error) {
268 0 : switch (tctx->error) {
269 : case ERANGE:
270 0 : ERROR("Could not allocate ID for the user - domain full?\n");
271 0 : break;
272 :
273 : case EEXIST:
274 0 : ERROR("A user or group with the same name or ID already exists\n");
275 0 : break;
276 :
277 : default:
278 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sysdb operation failed (%d)[%s]\n",
279 : tctx->error, strerror(tctx->error));
280 0 : ERROR("Transaction error. Could not add user.\n");
281 0 : break;
282 : }
283 0 : ret = EXIT_FAILURE;
284 0 : goto fini;
285 : }
286 :
287 0 : ret = EXIT_SUCCESS;
288 :
289 : fini:
290 0 : poptFreeContext(pc);
291 0 : talloc_free(tctx);
292 0 : free(groups);
293 0 : exit(ret);
294 : }
|