]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/chfn.c
chfn: fix coding style
[thirdparty/util-linux.git] / login-utils / chfn.c
1 /*
2 * chfn.c -- change your finger information
3 * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
4 *
5 * this program is free software. you can redistribute it and
6 * modify it under the terms of the gnu general public license.
7 * there is no warranty.
8 *
9 * $Author: aebr $
10 * $Revision: 1.18 $
11 * $Date: 1998/06/11 22:30:11 $
12 *
13 * Updated Thu Oct 12 09:19:26 1995 by faith@cs.unc.edu with security
14 * patches from Zefram <A.Main@dcs.warwick.ac.uk>
15 *
16 * Hacked by Peter Breitenlohner, peb@mppmu.mpg.de,
17 * to remove trailing empty fields. Oct 5, 96.
18 *
19 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
20 * - added Native Language Support
21 */
22
23 #include <ctype.h>
24 #include <errno.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33
34 #include "c.h"
35 #include "env.h"
36 #include "islocal.h"
37 #include "nls.h"
38 #include "pamfail.h"
39 #include "setpwnam.h"
40 #include "strutils.h"
41 #include "xalloc.h"
42
43 #ifdef HAVE_LIBSELINUX
44 # include <selinux/selinux.h>
45 # include <selinux/av_permissions.h>
46 # include "selinux_utils.h"
47 #endif
48
49 static char buf[1024];
50
51 struct finfo {
52 struct passwd *pw;
53 char *username;
54 char *full_name;
55 char *office;
56 char *office_phone;
57 char *home_phone;
58 char *other;
59 };
60
61 static int parse_argv(int argc, char *argv[], struct finfo *pinfo);
62 static void parse_passwd(struct passwd *pw, struct finfo *pinfo);
63 static void ask_info(struct finfo *oldfp, struct finfo *newfp);
64 static char *prompt(char *question, char *def_val);
65 static int check_gecos_string(char *msg, char *gecos);
66 static int set_changed_data(struct finfo *oldfp, struct finfo *newfp);
67 static int save_new_data(struct finfo *pinfo);
68
69 /* we do not accept gecos field sizes longer than MAX_FIELD_SIZE */
70 #define MAX_FIELD_SIZE 256
71
72 static void __attribute__((__noreturn__)) usage(FILE *fp)
73 {
74 fputs(USAGE_HEADER, fp);
75 fprintf(fp, _(" %s [options] [username]\n"), program_invocation_short_name);
76 fputs(USAGE_OPTIONS, fp);
77 fputs(_(" -f, --full-name <full-name> real name\n"), fp);
78 fputs(_(" -o, --office <office> office number\n"), fp);
79 fputs(_(" -p, --office-phone <phone> office phone number\n"), fp);
80 fputs(_(" -h, --home-phone <phone> home phone number\n"), fp);
81 fputs(USAGE_SEPARATOR, fp);
82 fputs(_(" -u, --help display this help and exit\n"), fp);
83 fputs(_(" -v, --version output version information and exit\n"), fp);
84 fprintf(fp, USAGE_MAN_TAIL("chfn(1)"));
85 exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
86 }
87
88 int main(int argc, char **argv)
89 {
90 uid_t uid;
91 struct finfo oldf, newf;
92 int interactive;
93 int status;
94
95 sanitize_env();
96 setlocale(LC_ALL, ""); /* both for messages and for iscntrl() below */
97 bindtextdomain(PACKAGE, LOCALEDIR);
98 textdomain(PACKAGE);
99
100 /*
101 * "oldf" contains the users original finger information.
102 * "newf" contains the changed finger information, and contains NULL
103 * in fields that haven't been changed.
104 * in the end, "newf" is folded into "oldf".
105 *
106 * the reason the new finger information is not put _immediately_
107 * into "oldf" is that on the command line, new finger information
108 * can be specified before we know what user the information is
109 * being specified for.
110 */
111 uid = getuid();
112 memset(&oldf, 0, sizeof(oldf));
113 memset(&newf, 0, sizeof(newf));
114
115 interactive = parse_argv(argc, argv, &newf);
116 if (!newf.username) {
117 parse_passwd(getpwuid(uid), &oldf);
118 if (!oldf.username)
119 errx(EXIT_FAILURE, _("you (user %d) don't exist."),
120 uid);
121 } else {
122 parse_passwd(getpwnam(newf.username), &oldf);
123 if (!oldf.username)
124 errx(EXIT_FAILURE, _("user \"%s\" does not exist."),
125 newf.username);
126 }
127
128 if (!(is_local(oldf.username)))
129 errx(EXIT_FAILURE, _("can only change local entries"));
130
131 #ifdef HAVE_LIBSELINUX
132 if (is_selinux_enabled() > 0) {
133 if (uid == 0) {
134 if (checkAccess(oldf.username, PASSWD__CHFN) != 0) {
135 security_context_t user_context;
136 if (getprevcon(&user_context) < 0)
137 user_context = NULL;
138 errx(EXIT_FAILURE,
139 _("%s is not authorized to change "
140 "the finger info of %s"),
141 user_context ? : _("Unknown user context"),
142 oldf.username);
143 }
144 }
145 if (setupDefaultContext("/etc/passwd"))
146 errx(EXIT_FAILURE,
147 _("can't set default context for /etc/passwd"));
148 }
149 #endif
150
151 /* Reality check */
152 if (uid != 0 && uid != oldf.pw->pw_uid) {
153 errno = EACCES;
154 err(EXIT_FAILURE, NULL);
155 }
156
157 printf(_("Changing finger information for %s.\n"), oldf.username);
158
159 #ifdef REQUIRE_PASSWORD
160 if (uid != 0) {
161 pam_handle_t *pamh = NULL;
162 struct pam_conv conv = { misc_conv, NULL };
163 int retcode;
164
165 retcode = pam_start("chfn", oldf.username, &conv, &pamh);
166 if (pam_fail_check(pamh, retcode))
167 exit(EXIT_FAILURE);
168
169 retcode = pam_authenticate(pamh, 0);
170 if (pam_fail_check(pamh, retcode))
171 exit(EXIT_FAILURE);
172
173 retcode = pam_acct_mgmt(pamh, 0);
174 if (retcode == PAM_NEW_AUTHTOK_REQD)
175 retcode =
176 pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
177 if (pam_fail_check(pamh, retcode))
178 exit(EXIT_FAILURE);
179
180 retcode = pam_setcred(pamh, 0);
181 if (pam_fail_check(pamh, retcode))
182 exit(EXIT_FAILURE);
183
184 pam_end(pamh, 0);
185 /* no need to establish a session; this isn't a
186 * session-oriented activity... */
187 }
188 #endif /* REQUIRE_PASSWORD */
189
190 if (interactive)
191 ask_info(&oldf, &newf);
192
193 if (!set_changed_data(&oldf, &newf)) {
194 printf(_("Finger information not changed.\n"));
195 return EXIT_SUCCESS;
196 }
197 status = save_new_data(&oldf);
198 return status;
199 }
200
201 /*
202 * parse_argv () --
203 * parse the command line arguments.
204 * returns true if no information beyond the username was given.
205 */
206 static int parse_argv(int argc, char *argv[], struct finfo *pinfo)
207 {
208 int index, c, status;
209 int info_given;
210
211 static struct option long_options[] = {
212 {"full-name", required_argument, 0, 'f'},
213 {"office", required_argument, 0, 'o'},
214 {"office-phone", required_argument, 0, 'p'},
215 {"home-phone", required_argument, 0, 'h'},
216 {"help", no_argument, 0, 'u'},
217 {"version", no_argument, 0, 'v'},
218 {NULL, no_argument, 0, '0'},
219 };
220
221 optind = 0;
222 info_given = false;
223 while (true) {
224 c = getopt_long(argc, argv, "f:r:p:h:o:uv", long_options,
225 &index);
226 if (c == -1)
227 break;
228 /* version? output version and exit. */
229 if (c == 'v') {
230 printf(UTIL_LINUX_VERSION);
231 exit(EXIT_SUCCESS);
232 }
233 if (c == 'u')
234 usage(stdout);
235 /* all other options must have an argument. */
236 if (!optarg)
237 usage(stderr);
238 /* ok, we were given an argument */
239 info_given = true;
240 status = 0;
241
242 /* now store the argument */
243 switch (c) {
244 case 'f':
245 pinfo->full_name = optarg;
246 status = check_gecos_string(_("Name"), optarg);
247 break;
248 case 'o':
249 pinfo->office = optarg;
250 status = check_gecos_string(_("Office"), optarg);
251 break;
252 case 'p':
253 pinfo->office_phone = optarg;
254 status = check_gecos_string(_("Office Phone"), optarg);
255 break;
256 case 'h':
257 pinfo->home_phone = optarg;
258 status = check_gecos_string(_("Home Phone"), optarg);
259 break;
260 default:
261 usage(stderr);
262 }
263 if (status < 0)
264 exit(status);
265 }
266 /* done parsing arguments. check for a username. */
267 if (optind < argc) {
268 if (optind + 1 < argc)
269 usage(stderr);
270 pinfo->username = argv[optind];
271 }
272 return (!info_given);
273 }
274
275 /*
276 * parse_passwd () --
277 * take a struct password and fill in the fields of the struct finfo.
278 */
279 static void parse_passwd(struct passwd *pw, struct finfo *pinfo)
280 {
281 char *gecos;
282 char *cp;
283
284 if (pw) {
285 pinfo->pw = pw;
286 pinfo->username = pw->pw_name;
287 /* use pw_gecos - we take a copy since PAM destroys the original */
288 gecos = xstrdup(pw->pw_gecos);
289 cp = (gecos ? gecos : "");
290 pinfo->full_name = cp;
291 cp = strchr(cp, ',');
292 if (cp) {
293 *cp = 0, cp++;
294 } else
295 return;
296 pinfo->office = cp;
297 cp = strchr(cp, ',');
298 if (cp) {
299 *cp = 0, cp++;
300 } else
301 return;
302 pinfo->office_phone = cp;
303 cp = strchr(cp, ',');
304 if (cp) {
305 *cp = 0, cp++;
306 } else
307 return;
308 pinfo->home_phone = cp;
309 /* extra fields contain site-specific information, and can
310 * not be changed by this version of chfn. */
311 cp = strchr(cp, ',');
312 if (cp) {
313 *cp = 0, cp++;
314 } else
315 return;
316 pinfo->other = cp;
317 }
318 }
319
320 /*
321 * ask_info () --
322 * prompt the user for the finger information and store it.
323 */
324 static void ask_info(struct finfo *oldfp, struct finfo *newfp)
325 {
326 newfp->full_name = prompt(_("Name"), oldfp->full_name);
327 newfp->office = prompt(_("Office"), oldfp->office);
328 newfp->office_phone = prompt(_("Office Phone"), oldfp->office_phone);
329 newfp->home_phone = prompt(_("Home Phone"), oldfp->home_phone);
330 printf("\n");
331 }
332
333 /*
334 * prompt () --
335 * ask the user for a given field and check that the string is legal.
336 */
337 static char *prompt(char *question, char *def_val)
338 {
339 static char *blank = "none";
340 int len;
341 char *ans, *cp;
342
343 while (true) {
344 if (!def_val)
345 def_val = "";
346 printf("%s [%s]: ", question, def_val);
347 *buf = 0;
348 if (fgets(buf, sizeof(buf), stdin) == NULL)
349 errx(EXIT_FAILURE, _("Aborted."));
350 /* remove the newline at the end of buf. */
351 ans = buf;
352 while (isspace(*ans))
353 ans++;
354 len = strlen(ans);
355 while (len > 0 && isspace(ans[len - 1]))
356 len--;
357 if (len <= 0)
358 return NULL;
359 ans[len] = 0;
360 if (!strcasecmp(ans, blank))
361 return "";
362 if (check_gecos_string(NULL, ans) >= 0)
363 break;
364 }
365 cp = (char *)xmalloc(len + 1);
366 strcpy(cp, ans);
367 return cp;
368 }
369
370 /*
371 * check_gecos_string () --
372 * check that the given gecos string is legal. if it's not legal,
373 * output "msg" followed by a description of the problem, and return (-1).
374 */
375 static int check_gecos_string(char *msg, char *gecos)
376 {
377 unsigned int i, c;
378
379 if (strlen(gecos) > MAX_FIELD_SIZE) {
380 if (msg)
381 warnx(_("field %s is too long"), msg);
382 else
383 warnx(_("field is too long"));
384 return -1;
385 }
386
387 for (i = 0; i < strlen(gecos); i++) {
388 c = gecos[i];
389 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
390 if (msg)
391 warnx(_("%s: '%c' is not allowed"), msg, c);
392 else
393 warnx(_("'%c' is not allowed"), c);
394 return -1;
395 }
396 if (iscntrl(c)) {
397 if (msg)
398 warnx(_
399 ("%s: control characters are not allowed"),
400 msg);
401 else
402 warnx(_("control characters are not allowed"));
403 return -1;
404 }
405 }
406 return 0;
407 }
408
409 /*
410 * set_changed_data () --
411 * incorporate the new data into the old finger info.
412 */
413 static int set_changed_data(struct finfo *oldfp, struct finfo *newfp)
414 {
415 int changed = false;
416
417 if (newfp->full_name) {
418 oldfp->full_name = newfp->full_name;
419 changed = true;
420 }
421 if (newfp->office) {
422 oldfp->office = newfp->office;
423 changed = true;
424 }
425 if (newfp->office_phone) {
426 oldfp->office_phone = newfp->office_phone;
427 changed = true;
428 }
429 if (newfp->home_phone) {
430 oldfp->home_phone = newfp->home_phone;
431 changed = true;
432 }
433
434 return changed;
435 }
436
437 /*
438 * save_new_data () --
439 * save the given finger info in /etc/passwd.
440 * return zero on success.
441 */
442 static int save_new_data(struct finfo *pinfo)
443 {
444 char *gecos;
445 int len;
446
447 /* null fields will confuse printf(). */
448 if (!pinfo->full_name)
449 pinfo->full_name = "";
450 if (!pinfo->office)
451 pinfo->office = "";
452 if (!pinfo->office_phone)
453 pinfo->office_phone = "";
454 if (!pinfo->home_phone)
455 pinfo->home_phone = "";
456 if (!pinfo->other)
457 pinfo->other = "";
458
459 /* create the new gecos string */
460 len = (strlen(pinfo->full_name) + strlen(pinfo->office) +
461 strlen(pinfo->office_phone) + strlen(pinfo->home_phone) +
462 strlen(pinfo->other) + 4);
463 gecos = (char *)xmalloc(len + 1);
464 sprintf(gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
465 pinfo->office_phone, pinfo->home_phone, pinfo->other);
466
467 /* remove trailing empty fields (but not subfields of pinfo->other) */
468 if (!pinfo->other[0]) {
469 while (len > 0 && gecos[len - 1] == ',')
470 len--;
471 gecos[len] = 0;
472 }
473
474 /* write the new struct passwd to the passwd file. */
475 pinfo->pw->pw_gecos = gecos;
476 if (setpwnam(pinfo->pw) < 0) {
477 warn("setpwnam");
478 printf(_
479 ("Finger information *NOT* changed. Try again later.\n"));
480 return -1;
481 }
482 printf(_("Finger information changed.\n"));
483 return 0;
484 }