]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/chfn.c
chfn: remove function prototypes
[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
7eda085c 69/* we do not accept gecos field sizes longer than MAX_FIELD_SIZE */
5c36a0eb
KZ
70#define MAX_FIELD_SIZE 256
71
39941b4e
MP
72static void __attribute__((__noreturn__)) usage(FILE *fp)
73{
3ca10299 74 fputs(USAGE_HEADER, fp);
09af3db4 75 fprintf(fp, _(" %s [options] [<username>]\n"), program_invocation_short_name);
3ca10299
SK
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);
db433bf7
SK
82 fputs(USAGE_HELP, fp);
83 fputs(USAGE_VERSION, fp);
3ca10299
SK
84 fprintf(fp, USAGE_MAN_TAIL("chfn(1)"));
85 exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
39941b4e
MP
86}
87
24016335
SK
88/*
89 * check_gecos_string () --
90 * check that the given gecos string is legal. if it's not legal,
91 * output "msg" followed by a description of the problem, and return (-1).
92 */
93static int check_gecos_string(char *msg, char *gecos)
3ca10299 94{
24016335 95 unsigned int i, c;
3ca10299 96
24016335
SK
97 if (strlen(gecos) > MAX_FIELD_SIZE) {
98 if (msg)
99 warnx(_("field %s is too long"), msg);
100 else
101 warnx(_("field is too long"));
102 return -1;
3ca10299
SK
103 }
104
24016335
SK
105 for (i = 0; i < strlen(gecos); i++) {
106 c = gecos[i];
107 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
108 if (msg)
109 warnx(_("%s: '%c' is not allowed"), msg, c);
110 else
111 warnx(_("'%c' is not allowed"), c);
112 return -1;
113 }
114 if (iscntrl(c)) {
115 if (msg)
116 warnx(_
117 ("%s: control characters are not allowed"),
118 msg);
119 else
120 warnx(_("control characters are not allowed"));
121 return -1;
3ca10299 122 }
3ca10299 123 }
24016335 124 return 0;
6dbe3af9
KZ
125}
126
127/*
128 * parse_argv () --
129 * parse the command line arguments.
130 * returns true if no information beyond the username was given.
131 */
8187b555 132static int parse_argv(int argc, char *argv[], struct finfo *pinfo)
6dbe3af9 133{
3ca10299
SK
134 int index, c, status;
135 int info_given;
136
137 static struct option long_options[] = {
138 {"full-name", required_argument, 0, 'f'},
139 {"office", required_argument, 0, 'o'},
140 {"office-phone", required_argument, 0, 'p'},
141 {"home-phone", required_argument, 0, 'h'},
142 {"help", no_argument, 0, 'u'},
143 {"version", no_argument, 0, 'v'},
144 {NULL, no_argument, 0, '0'},
145 };
146
147 optind = 0;
148 info_given = false;
149 while (true) {
150 c = getopt_long(argc, argv, "f:r:p:h:o:uv", long_options,
151 &index);
152 if (c == -1)
153 break;
154 /* version? output version and exit. */
155 if (c == 'v') {
156 printf(UTIL_LINUX_VERSION);
157 exit(EXIT_SUCCESS);
158 }
159 if (c == 'u')
160 usage(stdout);
161 /* all other options must have an argument. */
162 if (!optarg)
163 usage(stderr);
164 /* ok, we were given an argument */
165 info_given = true;
3ca10299
SK
166
167 /* now store the argument */
168 switch (c) {
169 case 'f':
170 pinfo->full_name = optarg;
171 status = check_gecos_string(_("Name"), optarg);
172 break;
173 case 'o':
174 pinfo->office = optarg;
175 status = check_gecos_string(_("Office"), optarg);
176 break;
177 case 'p':
178 pinfo->office_phone = optarg;
179 status = check_gecos_string(_("Office Phone"), optarg);
180 break;
181 case 'h':
182 pinfo->home_phone = optarg;
183 status = check_gecos_string(_("Home Phone"), optarg);
184 break;
185 default:
186 usage(stderr);
187 }
97da60c6
KZ
188 if (status != 0)
189 exit(EXIT_FAILURE);
6dbe3af9 190 }
3ca10299
SK
191 /* done parsing arguments. check for a username. */
192 if (optind < argc) {
193 if (optind + 1 < argc)
194 usage(stderr);
195 pinfo->username = argv[optind];
6dbe3af9 196 }
97da60c6 197 return !info_given;
6dbe3af9
KZ
198}
199
6dbe3af9
KZ
200/*
201 * parse_passwd () --
3ca10299 202 * take a struct password and fill in the fields of the struct finfo.
6dbe3af9 203 */
5b66cfae 204static void parse_passwd(struct passwd *pw, struct finfo *pinfo)
6dbe3af9 205{
3ca10299
SK
206 char *gecos;
207 char *cp;
208
209 if (pw) {
210 pinfo->pw = pw;
211 pinfo->username = pw->pw_name;
212 /* use pw_gecos - we take a copy since PAM destroys the original */
213 gecos = xstrdup(pw->pw_gecos);
214 cp = (gecos ? gecos : "");
215 pinfo->full_name = cp;
216 cp = strchr(cp, ',');
217 if (cp) {
218 *cp = 0, cp++;
219 } else
220 return;
221 pinfo->office = cp;
222 cp = strchr(cp, ',');
223 if (cp) {
224 *cp = 0, cp++;
225 } else
226 return;
227 pinfo->office_phone = cp;
228 cp = strchr(cp, ',');
229 if (cp) {
230 *cp = 0, cp++;
231 } else
232 return;
233 pinfo->home_phone = cp;
234 /* extra fields contain site-specific information, and can
235 * not be changed by this version of chfn. */
236 cp = strchr(cp, ',');
237 if (cp) {
238 *cp = 0, cp++;
239 } else
240 return;
241 pinfo->other = cp;
242 }
6dbe3af9
KZ
243}
244
6dbe3af9
KZ
245/*
246 * prompt () --
247 * ask the user for a given field and check that the string is legal.
248 */
5b66cfae 249static char *prompt(char *question, char *def_val)
6dbe3af9 250{
3ca10299
SK
251 static char *blank = "none";
252 int len;
253 char *ans, *cp;
254
255 while (true) {
256 if (!def_val)
257 def_val = "";
258 printf("%s [%s]: ", question, def_val);
259 *buf = 0;
260 if (fgets(buf, sizeof(buf), stdin) == NULL)
261 errx(EXIT_FAILURE, _("Aborted."));
262 /* remove the newline at the end of buf. */
263 ans = buf;
264 while (isspace(*ans))
265 ans++;
266 len = strlen(ans);
267 while (len > 0 && isspace(ans[len - 1]))
268 len--;
269 if (len <= 0)
270 return NULL;
271 ans[len] = 0;
272 if (!strcasecmp(ans, blank))
273 return "";
274 if (check_gecos_string(NULL, ans) >= 0)
275 break;
276 }
277 cp = (char *)xmalloc(len + 1);
278 strcpy(cp, ans);
279 return cp;
6dbe3af9
KZ
280}
281
282/*
24016335
SK
283 * ask_info () --
284 * prompt the user for the finger information and store it.
6dbe3af9 285 */
24016335 286static void ask_info(struct finfo *oldfp, struct finfo *newfp)
6dbe3af9 287{
24016335
SK
288 newfp->full_name = prompt(_("Name"), oldfp->full_name);
289 newfp->office = prompt(_("Office"), oldfp->office);
290 newfp->office_phone = prompt(_("Office Phone"), oldfp->office_phone);
291 newfp->home_phone = prompt(_("Home Phone"), oldfp->home_phone);
292 printf("\n");
6dbe3af9
KZ
293}
294
295/*
296 * set_changed_data () --
297 * incorporate the new data into the old finger info.
298 */
8187b555 299static int set_changed_data(struct finfo *oldfp, struct finfo *newfp)
6dbe3af9 300{
3ca10299
SK
301 int changed = false;
302
303 if (newfp->full_name) {
304 oldfp->full_name = newfp->full_name;
305 changed = true;
306 }
307 if (newfp->office) {
308 oldfp->office = newfp->office;
309 changed = true;
310 }
311 if (newfp->office_phone) {
312 oldfp->office_phone = newfp->office_phone;
313 changed = true;
314 }
315 if (newfp->home_phone) {
316 oldfp->home_phone = newfp->home_phone;
317 changed = true;
318 }
319
320 return changed;
6dbe3af9
KZ
321}
322
323/*
324 * save_new_data () --
325 * save the given finger info in /etc/passwd.
326 * return zero on success.
327 */
5b66cfae 328static int save_new_data(struct finfo *pinfo)
6dbe3af9 329{
3ca10299
SK
330 char *gecos;
331 int len;
332
333 /* null fields will confuse printf(). */
334 if (!pinfo->full_name)
335 pinfo->full_name = "";
336 if (!pinfo->office)
337 pinfo->office = "";
338 if (!pinfo->office_phone)
339 pinfo->office_phone = "";
340 if (!pinfo->home_phone)
341 pinfo->home_phone = "";
342 if (!pinfo->other)
343 pinfo->other = "";
344
345 /* create the new gecos string */
346 len = (strlen(pinfo->full_name) + strlen(pinfo->office) +
347 strlen(pinfo->office_phone) + strlen(pinfo->home_phone) +
348 strlen(pinfo->other) + 4);
349 gecos = (char *)xmalloc(len + 1);
350 sprintf(gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
351 pinfo->office_phone, pinfo->home_phone, pinfo->other);
352
353 /* remove trailing empty fields (but not subfields of pinfo->other) */
354 if (!pinfo->other[0]) {
355 while (len > 0 && gecos[len - 1] == ',')
356 len--;
357 gecos[len] = 0;
358 }
359
8c24b6aa 360#ifdef HAVE_LIBUSER
d86918b6
KZ
361 if (set_value_libuser("chfn", pinfo->pw->pw_name, pinfo->pw->pw_uid,
362 LU_GECOS, gecos) < 0) {
8c24b6aa 363#else /* HAVE_LIBUSER */
3ca10299
SK
364 /* write the new struct passwd to the passwd file. */
365 pinfo->pw->pw_gecos = gecos;
366 if (setpwnam(pinfo->pw) < 0) {
d86918b6 367 warn("setpwnam failed");
8c24b6aa 368#endif
3ca10299
SK
369 printf(_
370 ("Finger information *NOT* changed. Try again later.\n"));
371 return -1;
372 }
373 printf(_("Finger information changed.\n"));
374 return 0;
6dbe3af9 375}
24016335
SK
376
377int main(int argc, char **argv)
378{
379 uid_t uid;
380 struct finfo oldf, newf;
381 int interactive;
382
383 sanitize_env();
384 setlocale(LC_ALL, ""); /* both for messages and for iscntrl() below */
385 bindtextdomain(PACKAGE, LOCALEDIR);
386 textdomain(PACKAGE);
387 atexit(close_stdout);
388
389 /*
390 * "oldf" contains the users original finger information.
391 * "newf" contains the changed finger information, and contains NULL
392 * in fields that haven't been changed.
393 * in the end, "newf" is folded into "oldf".
394 *
395 * the reason the new finger information is not put _immediately_
396 * into "oldf" is that on the command line, new finger information
397 * can be specified before we know what user the information is
398 * being specified for.
399 */
400 uid = getuid();
401 memset(&oldf, 0, sizeof(oldf));
402 memset(&newf, 0, sizeof(newf));
403
404 interactive = parse_argv(argc, argv, &newf);
405 if (!newf.username) {
406 parse_passwd(getpwuid(uid), &oldf);
407 if (!oldf.username)
408 errx(EXIT_FAILURE, _("you (user %d) don't exist."),
409 uid);
410 } else {
411 parse_passwd(getpwnam(newf.username), &oldf);
412 if (!oldf.username)
413 errx(EXIT_FAILURE, _("user \"%s\" does not exist."),
414 newf.username);
415 }
416
417#ifndef HAVE_LIBUSER
418 if (!(is_local(oldf.username)))
419 errx(EXIT_FAILURE, _("can only change local entries"));
420#endif
421
422#ifdef HAVE_LIBSELINUX
423 if (is_selinux_enabled() > 0) {
424 if (uid == 0) {
425 if (checkAccess(oldf.username, PASSWD__CHFN) != 0) {
426 security_context_t user_context;
427 if (getprevcon(&user_context) < 0)
428 user_context = NULL;
429 errx(EXIT_FAILURE,
430 _("%s is not authorized to change "
431 "the finger info of %s"),
432 user_context ? : _("Unknown user context"),
433 oldf.username);
434 }
435 }
436 if (setupDefaultContext(_PATH_PASSWD))
437 errx(EXIT_FAILURE,
438 _("can't set default context for %s"), _PATH_PASSWD);
439 }
440#endif
441
442#ifdef HAVE_LIBUSER
443 /* If we're setuid and not really root, disallow the password change. */
444 if (geteuid() != getuid() && uid != oldf.pw->pw_uid) {
445#else
446 if (uid != 0 && uid != oldf.pw->pw_uid) {
447#endif
448 errno = EACCES;
449 err(EXIT_FAILURE, _("running UID doesn't match UID of user we're "
450 "altering, change denied"));
451 }
452
453 printf(_("Changing finger information for %s.\n"), oldf.username);
454
455#if !defined(HAVE_LIBUSER) && defined(CHFN_CHSH_PASSWORD)
456 if(!auth_pam("chfn", uid, oldf.username)) {
457 return EXIT_FAILURE;
458 }
459#endif
460
461 if (interactive)
462 ask_info(&oldf, &newf);
463
464 if (!set_changed_data(&oldf, &newf)) {
465 printf(_("Finger information not changed.\n"));
466 return EXIT_SUCCESS;
467 }
468
469 return save_new_data(&oldf) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
470}