2 * chfn.c -- change your finger information
3 * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
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.
11 * $Date: 1998/06/11 22:30:11 $
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>
16 * Hacked by Peter Breitenlohner, peb@mppmu.mpg.de,
17 * to remove trailing empty fields. Oct 5, 96.
19 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
20 * - added Native Language Support
25 #define _BSD_SOURCE /* for strcasecmp() */
27 #include <sys/types.h>
43 #ifdef HAVE_LIBSELINUX
44 #include <selinux/selinux.h>
45 #include <selinux/av_permissions.h>
46 #include "selinux_utils.h"
49 #if defined(REQUIRE_PASSWORD) && defined(HAVE_SECURITY_PAM_MISC_H)
50 #include <security/pam_appl.h>
51 #include <security/pam_misc.h>
53 #define PAM_FAIL_CHECK(_ph, _rc) \
55 if ((_rc) != PAM_SUCCESS) { \
56 fprintf(stderr, "\n%s\n", pam_strerror((_ph), (_rc))); \
57 pam_end((_ph), (_rc)); \
64 typedef unsigned char boolean;
70 static char buf[1024];
82 static boolean parse_argv (int argc, char *argv[], struct finfo *pinfo);
83 static void usage (FILE *fp);
84 static void parse_passwd (struct passwd *pw, struct finfo *pinfo);
85 static void ask_info (struct finfo *oldfp, struct finfo *newfp);
86 static char *prompt (char *question, char *def_val);
87 static int check_gecos_string (char *msg, char *gecos);
88 static boolean set_changed_data (struct finfo *oldfp, struct finfo *newfp);
89 static int save_new_data (struct finfo *pinfo);
90 static void *xmalloc (int bytes);
92 #define memzero(ptr, size) memset((char *) ptr, 0, size)
94 /* we do not accept gecos field sizes longer than MAX_FIELD_SIZE */
95 #define MAX_FIELD_SIZE 256
97 int main (int argc, char **argv) {
100 struct finfo oldf, newf;
105 setlocale(LC_ALL, ""); /* both for messages and for iscntrl() below */
106 bindtextdomain(PACKAGE, LOCALEDIR);
109 /* whoami is the program name for error messages */
111 if (! whoami) whoami = "chfn";
112 for (cp = whoami; *cp; cp++)
113 if (*cp == '/') whoami = cp + 1;
116 * "oldf" contains the users original finger information.
117 * "newf" contains the changed finger information, and contains NULL
118 * in fields that haven't been changed.
119 * in the end, "newf" is folded into "oldf".
120 * the reason the new finger information is not put _immediately_ into
121 * "oldf" is that on the command line, new finger information can
122 * be specified before we know what user the information is being
126 memzero (&oldf, sizeof (oldf));
127 memzero (&newf, sizeof (newf));
129 interactive = parse_argv (argc, argv, &newf);
130 if (! newf.username) {
131 parse_passwd (getpwuid (uid), &oldf);
132 if (! oldf.username) {
133 fprintf (stderr, _("%s: you (user %d) don't exist.\n"), whoami, uid);
137 parse_passwd (getpwnam (newf.username), &oldf);
138 if (! oldf.username) {
140 fprintf (stderr, _("%s: user \"%s\" does not exist.\n"), whoami, cp);
144 if (!(is_local(oldf.username))) {
145 fprintf (stderr, _("%s: can only change local entries; use yp%s instead.\n"),
150 #ifdef HAVE_LIBSELINUX
151 if (is_selinux_enabled() > 0) {
153 if (checkAccess(oldf.username,PASSWD__CHFN)!=0) {
154 security_context_t user_context;
155 if (getprevcon(&user_context) < 0)
156 user_context=(security_context_t) strdup(_("Unknown user context"));
157 fprintf(stderr, _("%s: %s is not authorized to change the finger info of %s\n"),
158 whoami, user_context, oldf.username);
159 freecon(user_context);
163 if (setupDefaultContext("/etc/passwd") != 0) {
164 fprintf(stderr,_("%s: Can't set default context for /etc/passwd"),
172 if (uid != 0 && uid != oldf.pw->pw_uid) {
178 printf (_("Changing finger information for %s.\n"), oldf.username);
180 #ifdef REQUIRE_PASSWORD
181 #ifdef HAVE_SECURITY_PAM_MISC_H
183 pam_handle_t *pamh = NULL;
184 struct pam_conv conv = { misc_conv, NULL };
187 retcode = pam_start("chfn", oldf.username, &conv, &pamh);
188 if(retcode != PAM_SUCCESS) {
189 fprintf(stderr, _("%s: PAM failure, aborting: %s\n"),
190 whoami, pam_strerror(pamh, retcode));
194 retcode = pam_authenticate(pamh, 0);
195 PAM_FAIL_CHECK(pamh, retcode);
197 retcode = pam_acct_mgmt(pamh, 0);
198 if (retcode == PAM_NEW_AUTHTOK_REQD)
199 retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
200 PAM_FAIL_CHECK(pamh, retcode);
202 retcode = pam_setcred(pamh, 0);
203 PAM_FAIL_CHECK(pamh, retcode);
206 /* no need to establish a session; this isn't a session-oriented
209 # else /* HAVE_SECURITY_PAM_MISC_H */
210 /* require password, unless root */
211 if(uid != 0 && oldf.pw->pw_passwd && oldf.pw->pw_passwd[0]) {
212 char *pwdstr = getpass(_("Password: "));
213 if(strncmp(oldf.pw->pw_passwd,
214 crypt(pwdstr, oldf.pw->pw_passwd), 13)) {
215 puts(_("Incorrect password."));
219 # endif /* HAVE_SECURITY_PAM_MISC_H */
220 #endif /* REQUIRE_PASSWORD */
223 if (interactive) ask_info (&oldf, &newf);
225 if (! set_changed_data (&oldf, &newf)) {
226 printf (_("Finger information not changed.\n"));
229 status = save_new_data (&oldf);
235 * parse the command line arguments.
236 * returns true if no information beyond the username was given.
238 static boolean parse_argv (argc, argv, pinfo)
243 int index, c, status;
246 static struct option long_options[] = {
247 { "full-name", required_argument, 0, 'f' },
248 { "office", required_argument, 0, 'o' },
249 { "office-phone", required_argument, 0, 'p' },
250 { "home-phone", required_argument, 0, 'h' },
251 { "help", no_argument, 0, 'u' },
252 { "version", no_argument, 0, 'v' },
253 { NULL, no_argument, 0, '0' },
259 c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
261 /* version? output version and exit. */
263 printf ("%s\n", PACKAGE_STRING);
270 /* all other options must have an argument. */
275 /* ok, we were given an argument */
279 xstrncpy (buf, whoami, sizeof(buf)-128);
282 /* now store the argument */
285 pinfo->full_name = optarg;
286 strcat (buf, "full name");
287 status = check_gecos_string (buf, optarg);
290 pinfo->office = optarg;
291 strcat (buf, "office");
292 status = check_gecos_string (buf, optarg);
295 pinfo->office_phone = optarg;
296 strcat (buf, "office phone");
297 status = check_gecos_string (buf, optarg);
300 pinfo->home_phone = optarg;
301 strcat (buf, "home phone");
302 status = check_gecos_string (buf, optarg);
308 if (status < 0) exit (status);
310 /* done parsing arguments. check for a username. */
312 if (optind + 1 < argc) {
316 pinfo->username = argv[optind];
318 return (! info_given);
323 * print out a usage message.
325 static void usage (fp)
328 fprintf (fp, _("Usage: %s [ -f full-name ] [ -o office ] "), whoami);
329 fprintf (fp, _("[ -p office-phone ]\n [ -h home-phone ] "));
330 fprintf (fp, _("[ --help ] [ --version ]\n"));
335 * take a struct password and fill in the fields of the
338 static void parse_passwd (pw, pinfo)
347 pinfo->username = pw->pw_name;
348 /* use pw_gecos - we take a copy since PAM destroys the original */
349 gecos = strdup(pw->pw_gecos);
350 cp = (gecos ? gecos : "");
351 pinfo->full_name = cp;
352 cp = strchr (cp, ',');
353 if (cp) { *cp = 0, cp++; } else return;
355 cp = strchr (cp, ',');
356 if (cp) { *cp = 0, cp++; } else return;
357 pinfo->office_phone = cp;
358 cp = strchr (cp, ',');
359 if (cp) { *cp = 0, cp++; } else return;
360 pinfo->home_phone = cp;
361 /* extra fields contain site-specific information, and
362 * can not be changed by this version of chfn. */
363 cp = strchr (cp, ',');
364 if (cp) { *cp = 0, cp++; } else return;
371 * prompt the user for the finger information and store it.
373 static void ask_info (oldfp, newfp)
377 newfp->full_name = prompt (_("Name"), oldfp->full_name);
378 newfp->office = prompt (_("Office"), oldfp->office);
379 newfp->office_phone = prompt (_("Office Phone"), oldfp->office_phone);
380 newfp->home_phone = prompt (_("Home Phone"), oldfp->home_phone);
386 * ask the user for a given field and check that the string is legal.
388 static char *prompt (question, def_val)
392 static char *blank = "none";
397 if (! def_val) def_val = "";
398 printf("%s [%s]: ", question, def_val);
400 if (fgets (buf, sizeof (buf), stdin) == NULL) {
401 printf (_("\nAborted.\n"));
404 /* remove the newline at the end of buf. */
406 while (isspace (*ans)) ans++;
408 while (len > 0 && isspace (ans[len-1])) len--;
409 if (len <= 0) return NULL;
411 if (! strcasecmp (ans, blank)) return "";
412 if (check_gecos_string (NULL, ans) >= 0) break;
414 cp = (char *) xmalloc (len + 1);
420 * check_gecos_string () --
421 * check that the given gecos string is legal. if it's not legal,
422 * output "msg" followed by a description of the problem, and
425 static int check_gecos_string (msg, gecos)
431 if (strlen(gecos) > MAX_FIELD_SIZE) {
434 printf(_("field is too long.\n"));
438 for (i = 0; i < strlen (gecos); i++) {
440 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
441 if (msg) printf ("%s: ", msg);
442 printf (_("'%c' is not allowed.\n"), c);
446 if (msg) printf ("%s: ", msg);
447 printf (_("Control characters are not allowed.\n"));
455 * set_changed_data () --
456 * incorporate the new data into the old finger info.
458 static boolean set_changed_data (oldfp, newfp)
462 boolean changed = false;
464 if (newfp->full_name) {
465 oldfp->full_name = newfp->full_name; changed = true; }
467 oldfp->office = newfp->office; changed = true; }
468 if (newfp->office_phone) {
469 oldfp->office_phone = newfp->office_phone; changed = true; }
470 if (newfp->home_phone) {
471 oldfp->home_phone = newfp->home_phone; changed = true; }
477 * save_new_data () --
478 * save the given finger info in /etc/passwd.
479 * return zero on success.
481 static int save_new_data (pinfo)
487 /* null fields will confuse printf(). */
488 if (! pinfo->full_name) pinfo->full_name = "";
489 if (! pinfo->office) pinfo->office = "";
490 if (! pinfo->office_phone) pinfo->office_phone = "";
491 if (! pinfo->home_phone) pinfo->home_phone = "";
492 if (! pinfo->other) pinfo->other = "";
494 /* create the new gecos string */
495 len = (strlen (pinfo->full_name) + strlen (pinfo->office) +
496 strlen (pinfo->office_phone) + strlen (pinfo->home_phone) +
497 strlen (pinfo->other) + 4);
498 gecos = (char *) xmalloc (len + 1);
499 sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
500 pinfo->office_phone, pinfo->home_phone, pinfo->other);
502 /* remove trailing empty fields (but not subfields of pinfo->other) */
503 if (! pinfo->other[0] ) {
504 while (len > 0 && gecos[len-1] == ',') len--;
508 /* write the new struct passwd to the passwd file. */
509 pinfo->pw->pw_gecos = gecos;
510 if (setpwnam (pinfo->pw) < 0) {
512 printf( _("Finger information *NOT* changed. Try again later.\n" ));
515 printf (_("Finger information changed.\n"));
520 * xmalloc () -- malloc that never fails.
522 static void *xmalloc (bytes)
528 if (! vp && bytes > 0) {
529 perror (_("malloc failed"));