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
31 #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 static char buf
[1024];
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
);
69 /* we do not accept gecos field sizes longer than MAX_FIELD_SIZE */
70 #define MAX_FIELD_SIZE 256
72 static void __attribute__((__noreturn__
)) usage(FILE *fp
)
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
);
88 int main(int argc
, char **argv
)
91 struct finfo oldf
, newf
;
96 setlocale(LC_ALL
, ""); /* both for messages and for iscntrl() below */
97 bindtextdomain(PACKAGE
, LOCALEDIR
);
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".
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.
112 memset(&oldf
, 0, sizeof(oldf
));
113 memset(&newf
, 0, sizeof(newf
));
115 interactive
= parse_argv(argc
, argv
, &newf
);
116 if (!newf
.username
) {
117 parse_passwd(getpwuid(uid
), &oldf
);
119 errx(EXIT_FAILURE
, _("you (user %d) don't exist."),
122 parse_passwd(getpwnam(newf
.username
), &oldf
);
124 errx(EXIT_FAILURE
, _("user \"%s\" does not exist."),
128 if (!(is_local(oldf
.username
)))
129 errx(EXIT_FAILURE
, _("can only change local entries"));
131 #ifdef HAVE_LIBSELINUX
132 if (is_selinux_enabled() > 0) {
134 if (checkAccess(oldf
.username
, PASSWD__CHFN
) != 0) {
135 security_context_t user_context
;
136 if (getprevcon(&user_context
) < 0)
139 _("%s is not authorized to change "
140 "the finger info of %s"),
141 user_context
? : _("Unknown user context"),
145 if (setupDefaultContext("/etc/passwd"))
147 _("can't set default context for /etc/passwd"));
152 if (uid
!= 0 && uid
!= oldf
.pw
->pw_uid
) {
154 err(EXIT_FAILURE
, NULL
);
157 printf(_("Changing finger information for %s.\n"), oldf
.username
);
159 #ifdef REQUIRE_PASSWORD
161 pam_handle_t
*pamh
= NULL
;
162 struct pam_conv conv
= { misc_conv
, NULL
};
165 retcode
= pam_start("chfn", oldf
.username
, &conv
, &pamh
);
166 if (pam_fail_check(pamh
, retcode
))
169 retcode
= pam_authenticate(pamh
, 0);
170 if (pam_fail_check(pamh
, retcode
))
173 retcode
= pam_acct_mgmt(pamh
, 0);
174 if (retcode
== PAM_NEW_AUTHTOK_REQD
)
176 pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
177 if (pam_fail_check(pamh
, retcode
))
180 retcode
= pam_setcred(pamh
, 0);
181 if (pam_fail_check(pamh
, retcode
))
185 /* no need to establish a session; this isn't a
186 * session-oriented activity... */
188 #endif /* REQUIRE_PASSWORD */
191 ask_info(&oldf
, &newf
);
193 if (!set_changed_data(&oldf
, &newf
)) {
194 printf(_("Finger information not changed.\n"));
197 status
= save_new_data(&oldf
);
203 * parse the command line arguments.
204 * returns true if no information beyond the username was given.
206 static int parse_argv(int argc
, char *argv
[], struct finfo
*pinfo
)
208 int index
, c
, status
;
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'},
224 c
= getopt_long(argc
, argv
, "f:r:p:h:o:uv", long_options
,
228 /* version? output version and exit. */
230 printf(UTIL_LINUX_VERSION
);
235 /* all other options must have an argument. */
238 /* ok, we were given an argument */
242 /* now store the argument */
245 pinfo
->full_name
= optarg
;
246 status
= check_gecos_string(_("Name"), optarg
);
249 pinfo
->office
= optarg
;
250 status
= check_gecos_string(_("Office"), optarg
);
253 pinfo
->office_phone
= optarg
;
254 status
= check_gecos_string(_("Office Phone"), optarg
);
257 pinfo
->home_phone
= optarg
;
258 status
= check_gecos_string(_("Home Phone"), optarg
);
266 /* done parsing arguments. check for a username. */
268 if (optind
+ 1 < argc
)
270 pinfo
->username
= argv
[optind
];
272 return (!info_given
);
277 * take a struct password and fill in the fields of the struct finfo.
279 static void parse_passwd(struct passwd
*pw
, struct finfo
*pinfo
)
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
, ',');
297 cp
= strchr(cp
, ',');
302 pinfo
->office_phone
= cp
;
303 cp
= strchr(cp
, ',');
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
, ',');
322 * prompt the user for the finger information and store it.
324 static void ask_info(struct finfo
*oldfp
, struct finfo
*newfp
)
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
);
335 * ask the user for a given field and check that the string is legal.
337 static char *prompt(char *question
, char *def_val
)
339 static char *blank
= "none";
346 printf("%s [%s]: ", question
, def_val
);
348 if (fgets(buf
, sizeof(buf
), stdin
) == NULL
)
349 errx(EXIT_FAILURE
, _("Aborted."));
350 /* remove the newline at the end of buf. */
352 while (isspace(*ans
))
355 while (len
> 0 && isspace(ans
[len
- 1]))
360 if (!strcasecmp(ans
, blank
))
362 if (check_gecos_string(NULL
, ans
) >= 0)
365 cp
= (char *)xmalloc(len
+ 1);
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).
375 static int check_gecos_string(char *msg
, char *gecos
)
379 if (strlen(gecos
) > MAX_FIELD_SIZE
) {
381 warnx(_("field %s is too long"), msg
);
383 warnx(_("field is too long"));
387 for (i
= 0; i
< strlen(gecos
); i
++) {
389 if (c
== ',' || c
== ':' || c
== '=' || c
== '"' || c
== '\n') {
391 warnx(_("%s: '%c' is not allowed"), msg
, c
);
393 warnx(_("'%c' is not allowed"), c
);
399 ("%s: control characters are not allowed"),
402 warnx(_("control characters are not allowed"));
410 * set_changed_data () --
411 * incorporate the new data into the old finger info.
413 static int set_changed_data(struct finfo
*oldfp
, struct finfo
*newfp
)
417 if (newfp
->full_name
) {
418 oldfp
->full_name
= newfp
->full_name
;
422 oldfp
->office
= newfp
->office
;
425 if (newfp
->office_phone
) {
426 oldfp
->office_phone
= newfp
->office_phone
;
429 if (newfp
->home_phone
) {
430 oldfp
->home_phone
= newfp
->home_phone
;
438 * save_new_data () --
439 * save the given finger info in /etc/passwd.
440 * return zero on success.
442 static int save_new_data(struct finfo
*pinfo
)
447 /* null fields will confuse printf(). */
448 if (!pinfo
->full_name
)
449 pinfo
->full_name
= "";
452 if (!pinfo
->office_phone
)
453 pinfo
->office_phone
= "";
454 if (!pinfo
->home_phone
)
455 pinfo
->home_phone
= "";
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
);
467 /* remove trailing empty fields (but not subfields of pinfo->other) */
468 if (!pinfo
->other
[0]) {
469 while (len
> 0 && gecos
[len
- 1] == ',')
474 /* write the new struct passwd to the passwd file. */
475 pinfo
->pw
->pw_gecos
= gecos
;
476 if (setpwnam(pinfo
->pw
) < 0) {
479 ("Finger information *NOT* changed. Try again later.\n"));
482 printf(_("Finger information changed.\n"));