Line data Source code
1 : /*
2 : SSSD
3 :
4 : sss_usermod
5 :
6 : Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <talloc.h>
25 : #include <popt.h>
26 : #include <errno.h>
27 : #include <pwd.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 : int pc_lock = 0;
38 0 : uid_t pc_uid = 0;
39 0 : gid_t pc_gid = 0;
40 0 : char *pc_gecos = NULL;
41 0 : char *pc_home = NULL;
42 0 : char *pc_shell = NULL;
43 0 : int pc_debug = SSSDBG_DEFAULT;
44 0 : const char *pc_selinux_user = NULL;
45 0 : struct poptOption long_options[] = {
46 : POPT_AUTOHELP
47 0 : { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0, _("The debug level to run with"), NULL },
48 0 : { "uid", 'u', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_uid, 0, _("The UID of the user"), NULL },
49 0 : { "gid", 'g', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_gid, 0, _("The GID 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 : { "append-group", 'a', POPT_ARG_STRING, NULL, 'a', _("Groups to add this user to"), NULL },
54 0 : { "remove-group", 'r', POPT_ARG_STRING, NULL, 'r', _("Groups to remove this user from"), NULL },
55 0 : { "lock", 'L', POPT_ARG_NONE, NULL, 'L', _("Lock the account"), NULL },
56 0 : { "unlock", 'U', POPT_ARG_NONE, NULL, 'U', _("Unlock the account"), NULL },
57 0 : { "addattr", '\0', POPT_ARG_STRING, NULL, 't' , _("Add an attribute/value pair. The format is attrname=value."), NULL },
58 0 : { "delattr", '\0', POPT_ARG_STRING, NULL, 'd' , _("Delete an attribute/value pair. The format is attrname=value."), NULL },
59 0 : { "setattr", '\0', POPT_ARG_STRING, NULL, 's' , _("Set an attribute to a name/value pair. The format is attrname=value. For multi-valued attributes, the command replaces the values already present"), NULL },
60 0 : { "selinux-user", 'Z', POPT_ARG_STRING, &pc_selinux_user, 0, _("The SELinux user for user's login"), NULL },
61 : POPT_TABLEEND
62 : };
63 0 : poptContext pc = NULL;
64 0 : char *addgroups = NULL, *rmgroups = NULL;
65 0 : char *addattr = NULL, *delattr = NULL, *setattr = NULL;
66 : int ret;
67 : errno_t sret;
68 0 : const char *pc_username = NULL;
69 0 : struct tools_ctx *tctx = NULL;
70 0 : char *badgroup = NULL;
71 0 : bool in_transaction = false;
72 :
73 0 : debug_prg_name = argv[0];
74 :
75 0 : ret = set_locale();
76 0 : if (ret != EOK) {
77 0 : DEBUG(SSSDBG_CRIT_FAILURE,
78 : "set_locale failed (%d): %s\n", ret, strerror(ret));
79 0 : ERROR("Error setting the locale\n");
80 0 : ret = EXIT_FAILURE;
81 0 : goto fini;
82 : }
83 :
84 : /* parse parameters */
85 0 : pc = poptGetContext(NULL, argc, argv, long_options, 0);
86 0 : poptSetOtherOptionHelp(pc, "USERNAME");
87 0 : while ((ret = poptGetNextOpt(pc)) > 0) {
88 0 : switch (ret) {
89 : case 'a':
90 0 : addgroups = poptGetOptArg(pc);
91 0 : if (addgroups == NULL) {
92 0 : BAD_POPT_PARAMS(pc, _("Specify group to add to\n"),
93 : ret, fini);
94 : }
95 0 : break;
96 :
97 : case 'r':
98 0 : rmgroups = poptGetOptArg(pc);
99 0 : if (rmgroups == NULL) {
100 0 : BAD_POPT_PARAMS(pc, _("Specify group to remove from\n"),
101 : ret, fini);
102 : }
103 0 : break;
104 :
105 : case 'L':
106 0 : pc_lock = DO_LOCK;
107 0 : break;
108 :
109 : case 'U':
110 0 : pc_lock = DO_UNLOCK;
111 0 : break;
112 :
113 : case 't':
114 0 : addattr = poptGetOptArg(pc);
115 0 : if (addattr == NULL) {
116 0 : BAD_POPT_PARAMS(pc,
117 : _("Specify the attribute name/value pair(s)\n"),
118 : ret, fini);
119 : }
120 0 : break;
121 :
122 : case 'd':
123 0 : delattr = poptGetOptArg(pc);
124 0 : if (delattr == NULL) {
125 0 : BAD_POPT_PARAMS(pc,
126 : _("Specify the attribute name/value pair(s)\n"),
127 : ret, fini);
128 : }
129 0 : break;
130 :
131 : case 's':
132 0 : setattr = poptGetOptArg(pc);
133 0 : if (setattr == NULL) {
134 0 : BAD_POPT_PARAMS(pc,
135 : _("Specify the attribute name/value pair(s)\n"),
136 : ret, fini);
137 : }
138 0 : break;
139 :
140 : }
141 : }
142 :
143 0 : if (ret != -1) {
144 0 : BAD_POPT_PARAMS(pc, poptStrerror(ret), ret, fini);
145 : }
146 :
147 0 : DEBUG_CLI_INIT(pc_debug);
148 :
149 : /* username is an argument without --option */
150 0 : pc_username = poptGetArg(pc);
151 0 : if (pc_username == NULL) {
152 0 : BAD_POPT_PARAMS(pc, _("Specify user to modify\n"), ret, fini);
153 : }
154 :
155 0 : CHECK_ROOT(ret, debug_prg_name);
156 :
157 0 : ret = init_sss_tools(&tctx);
158 0 : if (ret != EOK) {
159 0 : DEBUG(SSSDBG_CRIT_FAILURE,
160 : "init_sss_tools failed (%d): %s\n", ret, strerror(ret));
161 0 : if (ret == ENOENT) {
162 0 : ERROR("Error initializing the tools - no local domain\n");
163 : } else {
164 0 : ERROR("Error initializing the tools\n");
165 : }
166 0 : ret = EXIT_FAILURE;
167 0 : goto fini;
168 : }
169 :
170 : /* if the domain was not given as part of FQDN, default to local domain */
171 0 : ret = parse_name_domain(tctx, pc_username);
172 0 : if (ret != EOK) {
173 0 : ERROR("Invalid domain specified in FQDN\n");
174 0 : ret = EXIT_FAILURE;
175 0 : goto fini;
176 : }
177 : /* check the username to be able to give sensible error message */
178 0 : ret = sysdb_getpwnam_sync(tctx, tctx->octx->name, tctx->octx);
179 0 : if (ret != EOK) {
180 0 : ERROR("Cannot find user in local domain, "
181 : "modifying users is allowed only in local domain\n");
182 0 : ret = EXIT_FAILURE;
183 0 : goto fini;
184 : }
185 :
186 0 : if (id_in_range(tctx->octx->uid, tctx->octx->domain) != EOK) {
187 0 : ERROR("The selected UID is outside the allowed range\n");
188 0 : ret = EXIT_FAILURE;
189 0 : goto fini;
190 : }
191 :
192 0 : if (addgroups) {
193 0 : ret = parse_groups(tctx, addgroups, &tctx->octx->addgroups);
194 0 : if (ret != EOK) {
195 0 : DEBUG(SSSDBG_CRIT_FAILURE,
196 : "Cannot parse groups to add the user to\n");
197 0 : ERROR("Internal error while parsing parameters\n");
198 0 : ret = EXIT_FAILURE;
199 0 : goto fini;
200 : }
201 :
202 0 : ret = parse_group_name_domain(tctx, tctx->octx->addgroups);
203 0 : if (ret != EOK) {
204 0 : DEBUG(SSSDBG_CRIT_FAILURE,
205 : "Cannot parse FQDN groups to add the user to\n");
206 0 : ERROR("Groups must be in the same domain as user\n");
207 0 : ret = EXIT_FAILURE;
208 0 : goto fini;
209 : }
210 :
211 : /* Check group names in the LOCAL domain */
212 0 : ret = check_group_names(tctx, tctx->octx->addgroups, &badgroup);
213 0 : if (ret != EOK) {
214 0 : ERROR("Cannot find group %1$s in local domain, "
215 : "only groups in local domain are allowed\n", badgroup);
216 0 : ret = EXIT_FAILURE;
217 0 : goto fini;
218 : }
219 : }
220 :
221 0 : if (rmgroups) {
222 0 : ret = parse_groups(tctx, rmgroups, &tctx->octx->rmgroups);
223 0 : if (ret != EOK) {
224 0 : DEBUG(SSSDBG_CRIT_FAILURE,
225 : "Cannot parse groups to remove the user from\n");
226 0 : ERROR("Internal error while parsing parameters\n");
227 0 : ret = EXIT_FAILURE;
228 0 : goto fini;
229 : }
230 :
231 0 : ret = parse_group_name_domain(tctx, tctx->octx->rmgroups);
232 0 : if (ret != EOK) {
233 0 : DEBUG(SSSDBG_CRIT_FAILURE,
234 : "Cannot parse FQDN groups to remove the user from\n");
235 0 : ERROR("Groups must be in the same domain as user\n");
236 0 : ret = EXIT_FAILURE;
237 0 : goto fini;
238 : }
239 :
240 : /* Check group names in the LOCAL domain */
241 0 : ret = check_group_names(tctx, tctx->octx->rmgroups, &badgroup);
242 0 : if (ret != EOK) {
243 0 : ERROR("Cannot find group %1$s in local domain, "
244 : "only groups in local domain are allowed\n", badgroup);
245 0 : ret = EXIT_FAILURE;
246 0 : goto fini;
247 : }
248 : }
249 :
250 0 : tctx->octx->gecos = pc_gecos;
251 0 : tctx->octx->home = pc_home;
252 0 : tctx->octx->shell = pc_shell;
253 0 : tctx->octx->uid = pc_uid;
254 0 : tctx->octx->gid = pc_gid;
255 0 : tctx->octx->lock = pc_lock;
256 0 : tctx->octx->addattr = addattr;
257 0 : tctx->octx->delattr = delattr;
258 0 : tctx->octx->setattr = setattr;
259 :
260 0 : tctx->error = sysdb_transaction_start(tctx->sysdb);
261 0 : if (tctx->error != EOK) {
262 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
263 0 : goto done;
264 : }
265 0 : in_transaction = true;
266 :
267 : /* usermod */
268 0 : tctx->error = usermod(tctx, tctx->octx);
269 0 : if (tctx->error) {
270 0 : goto done;
271 : }
272 :
273 0 : tctx->error = sysdb_transaction_commit(tctx->sysdb);
274 0 : if (tctx->error) {
275 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
276 0 : goto done;
277 : }
278 0 : in_transaction = false;
279 :
280 0 : ret = sss_mc_refresh_user(pc_username);
281 0 : if (ret != EOK) {
282 0 : ERROR("NSS request failed (%1$d). Entry might remain in memory "
283 : "cache.\n", ret);
284 : /* Nothing we can do about it */
285 : }
286 :
287 0 : ret = sss_mc_refresh_grouplist(tctx, tctx->octx->addgroups);
288 0 : if (ret != EOK) {
289 0 : ERROR("NSS request failed (%1$d). Entry might remain in memory "
290 : "cache.\n", ret);
291 : /* Nothing we can do about it */
292 : }
293 :
294 0 : ret = sss_mc_refresh_grouplist(tctx, tctx->octx->rmgroups);
295 0 : if (ret != EOK) {
296 0 : ERROR("NSS request failed (%1$d). Entry might remain in memory "
297 : "cache.\n", ret);
298 : /* Nothing we can do about it */
299 : }
300 :
301 : /* Set SELinux login context - must be done after transaction is done
302 : * b/c libselinux calls getpwnam */
303 0 : ret = set_seuser(tctx->octx->name, pc_selinux_user, NULL);
304 0 : if (ret != EOK) {
305 0 : ERROR("Cannot set SELinux login context\n");
306 0 : ret = EXIT_FAILURE;
307 0 : goto fini;
308 : }
309 :
310 : done:
311 0 : if (in_transaction) {
312 0 : sret = sysdb_transaction_cancel(tctx->sysdb);
313 0 : if (sret != EOK) {
314 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
315 : }
316 : }
317 :
318 0 : if (tctx->error) {
319 0 : ret = tctx->error;
320 0 : switch (ret) {
321 : case ENOENT:
322 0 : ERROR("Could not modify user - check if group names are correct\n");
323 0 : break;
324 :
325 : case EFAULT:
326 0 : ERROR("Could not modify user - user already member of groups?\n");
327 0 : break;
328 :
329 : default:
330 0 : ERROR("Transaction error. Could not modify user.\n");
331 0 : break;
332 : }
333 :
334 0 : ret = EXIT_FAILURE;
335 0 : goto fini;
336 : }
337 :
338 0 : ret = EXIT_SUCCESS;
339 :
340 : fini:
341 0 : free(addgroups);
342 0 : free(rmgroups);
343 0 : poptFreeContext(pc);
344 0 : talloc_free(tctx);
345 0 : exit(ret);
346 : }
|