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