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 allow peaceful coexistence with yp: using the changes by
18 * Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es, from
19 * passwd.c (now moved to setpwnam.c);
20 * to remove trailing empty fields. Oct 5, 96.
24 #define _BSD_SOURCE /* for strcasecmp() */
26 #include <sys/types.h>
36 #include "../version.h"
38 #if REQUIRE_PASSWORD && USE_PAM
39 #include <security/pam_appl.h>
40 #include <security/pam_misc.h>
43 extern int is_local(char *);
52 typedef unsigned char boolean
;
58 static char buf
[1024];
70 static boolean parse_argv
P((int argc
, char *argv
[], struct finfo
*pinfo
));
71 static void usage
P((FILE *fp
));
72 static void parse_passwd
P((struct passwd
*pw
, struct finfo
*pinfo
));
73 static void ask_info
P((struct finfo
*oldfp
, struct finfo
*newfp
));
74 static char *prompt
P((char *question
, char *def_val
));
75 static int check_gecos_string
P((char *msg
, char *gecos
));
76 static boolean set_changed_data
P((struct finfo
*oldfp
, struct finfo
*newfp
));
77 static int save_new_data
P((struct finfo
*pinfo
));
78 static void *xmalloc
P((int bytes
));
80 extern int setpwnam
P((struct passwd
*pwd
));
82 #define memzero(ptr, size) memset((char *) ptr, 0, size)
90 struct finfo oldf
, newf
;
94 #if REQUIRE_PASSWORD && USE_PAM
95 pam_handle_t
*pamh
= NULL
;
97 struct pam_conv conv
= { misc_conv
, NULL
};
100 /* whoami is the program name for error messages */
102 if (! whoami
) whoami
= "chfn";
103 for (cp
= whoami
; *cp
; cp
++)
104 if (*cp
== '/') whoami
= cp
+ 1;
107 * "oldf" contains the users original finger information.
108 * "newf" contains the changed finger information, and contains NULL
109 * in fields that haven't been changed.
110 * in the end, "newf" is folded into "oldf".
111 * the reason the new finger information is not put _immediately_ into
112 * "oldf" is that on the command line, new finger information can
113 * be specified before we know what user the information is being
117 memzero (&oldf
, sizeof (oldf
));
118 memzero (&newf
, sizeof (newf
));
120 interactive
= parse_argv (argc
, argv
, &newf
);
121 if (! newf
.username
) {
122 parse_passwd (getpwuid (uid
), &oldf
);
123 if (! oldf
.username
) {
124 fprintf (stderr
, "%s: you (user %d) don't exist.\n", whoami
, uid
);
128 parse_passwd (getpwnam (newf
.username
), &oldf
);
129 if (! oldf
.username
) {
131 fprintf (stderr
, "%s: user \"%s\" does not exist.\n", whoami
, cp
);
135 if (!(is_local(oldf
.username
))) {
136 fprintf (stderr
, "%s: can only change local entries; use yp%s instead.\n",
142 if (uid
!= 0 && uid
!= oldf
.pw
->pw_uid
) {
148 printf ("Changing finger information for %s.\n", oldf
.username
);
153 if (pam_start("chfn", oldf
.username
, &conv
, &pamh
)) {
154 puts("Password error.");
157 if (pam_authenticate(pamh
, 0)) {
158 puts("Password error.");
161 retcode
= pam_acct_mgmt(pamh
, 0);
162 if (retcode
== PAM_NEW_AUTHTOK_REQD
) {
163 retcode
= pam_chauthtok(pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
164 } else if (retcode
) {
165 puts("Password error.");
168 if (pam_setcred(pamh
, 0)) {
169 puts("Password error.");
172 /* no need to establish a session; this isn't a session-oriented
176 /* require password, unless root */
177 if(uid
!= 0 && oldf
.pw
->pw_passwd
&& oldf
.pw
->pw_passwd
[0]) {
178 pwdstr
= getpass("Password: ");
179 if(strncmp(oldf
.pw
->pw_passwd
,
180 crypt(pwdstr
, oldf
.pw
->pw_passwd
), 13)) {
181 puts("Incorrect password.");
185 # endif /* USE_PAM */
186 #endif /* REQUIRE_PASSWORD */
189 if (interactive
) ask_info (&oldf
, &newf
);
191 if (! set_changed_data (&oldf
, &newf
)) {
192 printf ("Finger information not changed.\n");
195 status
= save_new_data (&oldf
);
201 * parse the command line arguments.
202 * returns true if no information beyond the username was given.
204 static boolean
parse_argv (argc
, argv
, pinfo
)
209 int index
, c
, status
;
212 static struct option long_options
[] = {
213 { "full-name", required_argument
, 0, 'f' },
214 { "office", required_argument
, 0, 'o' },
215 { "office-phone", required_argument
, 0, 'p' },
216 { "home-phone", required_argument
, 0, 'h' },
217 { "help", no_argument
, 0, 'u' },
218 { "version", no_argument
, 0, 'v' },
219 { NULL
, no_argument
, 0, '0' },
225 c
= getopt_long (argc
, argv
, "f:r:p:h:o:uv", long_options
, &index
);
227 /* version? output version and exit. */
229 printf ("%s\n", util_linux_version
);
236 /* all other options must have an argument. */
241 /* ok, we were given an argument */
245 strncpy (buf
, whoami
, sizeof(buf
)-128);
246 buf
[sizeof(buf
)-128-1] = 0;
249 /* now store the argument */
252 pinfo
->full_name
= optarg
;
253 strcat (buf
, "full name");
254 status
= check_gecos_string (buf
, optarg
);
257 pinfo
->office
= optarg
;
258 strcat (buf
, "office");
259 status
= check_gecos_string (buf
, optarg
);
262 pinfo
->office_phone
= optarg
;
263 strcat (buf
, "office phone");
264 status
= check_gecos_string (buf
, optarg
);
267 pinfo
->home_phone
= optarg
;
268 strcat (buf
, "home phone");
269 status
= check_gecos_string (buf
, optarg
);
275 if (status
< 0) exit (status
);
277 /* done parsing arguments. check for a username. */
279 if (optind
+ 1 < argc
) {
283 pinfo
->username
= argv
[optind
];
285 return (! info_given
);
290 * print out a usage message.
292 static void usage (fp
)
295 fprintf (fp
, "Usage: %s [ -f full-name ] [ -o office ] ", whoami
);
296 fprintf (fp
, "[ -p office-phone ]\n [ -h home-phone ] ");
297 fprintf (fp
, "[ --help ] [ --version ]\n");
302 * take a struct password and fill in the fields of the
305 static void parse_passwd (pw
, pinfo
)
313 pinfo
->username
= pw
->pw_name
;
316 pinfo
->full_name
= cp
;
317 cp
= strchr (cp
, ',');
318 if (cp
) { *cp
= 0, cp
++; } else return;
320 cp
= strchr (cp
, ',');
321 if (cp
) { *cp
= 0, cp
++; } else return;
322 pinfo
->office_phone
= cp
;
323 cp
= strchr (cp
, ',');
324 if (cp
) { *cp
= 0, cp
++; } else return;
325 pinfo
->home_phone
= cp
;
326 /* extra fields contain site-specific information, and
327 * can not be changed by this version of chfn. */
328 cp
= strchr (cp
, ',');
329 if (cp
) { *cp
= 0, cp
++; } else return;
336 * prompt the user for the finger information and store it.
338 static void ask_info (oldfp
, newfp
)
342 newfp
->full_name
= prompt ("Name", oldfp
->full_name
);
343 newfp
->office
= prompt ("Office", oldfp
->office
);
344 newfp
->office_phone
= prompt ("Office Phone", oldfp
->office_phone
);
345 newfp
->home_phone
= prompt ("Home Phone", oldfp
->home_phone
);
351 * ask the user for a given field and check that the string is legal.
353 static char *prompt (question
, def_val
)
357 static char *blank
= "none";
362 if (! def_val
) def_val
= "";
363 printf("%s [%s]: ", question
, def_val
);
365 if (fgets (buf
, sizeof (buf
), stdin
) == NULL
) {
366 printf ("\nAborted.\n");
369 /* remove the newline at the end of buf. */
371 while (isspace (*ans
)) ans
++;
373 while (len
> 0 && isspace (ans
[len
-1])) len
--;
374 if (len
<= 0) return NULL
;
376 if (! strcasecmp (ans
, blank
)) return "";
377 if (check_gecos_string (NULL
, ans
) >= 0) break;
379 cp
= (char *) xmalloc (len
+ 1);
385 * check_gecos_string () --
386 * check that the given gecos string is legal. if it's not legal,
387 * output "msg" followed by a description of the problem, and
390 static int check_gecos_string (msg
, gecos
)
396 for (i
= 0; i
< strlen (gecos
); i
++) {
398 if (c
== ',' || c
== ':' || c
== '=' || c
== '"' || c
== '\n') {
399 if (msg
) printf ("%s: ", msg
);
400 printf ("'%c' is not allowed.\n", c
);
404 if (msg
) printf ("%s: ", msg
);
405 printf ("Control characters are not allowed.\n");
413 * set_changed_data () --
414 * incorporate the new data into the old finger info.
416 static boolean
set_changed_data (oldfp
, newfp
)
420 boolean changed
= false;
422 if (newfp
->full_name
) {
423 oldfp
->full_name
= newfp
->full_name
; changed
= true; }
425 oldfp
->office
= newfp
->office
; changed
= true; }
426 if (newfp
->office_phone
) {
427 oldfp
->office_phone
= newfp
->office_phone
; changed
= true; }
428 if (newfp
->home_phone
) {
429 oldfp
->home_phone
= newfp
->home_phone
; changed
= true; }
435 * save_new_data () --
436 * save the given finger info in /etc/passwd.
437 * return zero on success.
439 static int save_new_data (pinfo
)
445 /* null fields will confuse printf(). */
446 if (! pinfo
->full_name
) pinfo
->full_name
= "";
447 if (! pinfo
->office
) pinfo
->office
= "";
448 if (! pinfo
->office_phone
) pinfo
->office_phone
= "";
449 if (! pinfo
->home_phone
) pinfo
->home_phone
= "";
450 if (! pinfo
->other
) pinfo
->other
= "";
452 /* create the new gecos string */
453 len
= (strlen (pinfo
->full_name
) + strlen (pinfo
->office
) +
454 strlen (pinfo
->office_phone
) + strlen (pinfo
->home_phone
) +
455 strlen (pinfo
->other
) + 4);
456 gecos
= (char *) xmalloc (len
+ 1);
457 sprintf (gecos
, "%s,%s,%s,%s,%s", pinfo
->full_name
, pinfo
->office
,
458 pinfo
->office_phone
, pinfo
->home_phone
, pinfo
->other
);
460 /* remove trailing empty fields (but not subfields of pinfo->other) */
461 if (! pinfo
->other
[0] ) {
462 while (len
> 0 && gecos
[len
-1] == ',') len
--;
466 /* write the new struct passwd to the passwd file. */
467 pinfo
->pw
->pw_gecos
= gecos
;
468 if (setpwnam (pinfo
->pw
) < 0) {
470 printf( "Finger information *NOT* changed. Try again later.\n" );
473 printf ("Finger information changed.\n");
478 * xmalloc () -- malloc that never fails.
480 static void *xmalloc (bytes
)
486 if (! vp
&& bytes
> 0) {
487 perror ("malloc failed");