]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/chfn.c
Imported from util-linux-2.8 tarball.
[thirdparty/util-linux.git] / login-utils / chfn.c
1 /*
2 * chfn.c -- change your finger information
3 * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
4 *
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.
8 *
9 * $Author: aebr $
10 * $Revision: 1.18 $
11 * $Date: 1998/06/11 22:30:11 $
12 *
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>
15 *
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.
21 *
22 */
23
24 #define _BSD_SOURCE /* for strcasecmp() */
25
26 #include <sys/types.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <pwd.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <getopt.h>
35 #include "my_crypt.h"
36 #include "../version.h"
37
38 #if REQUIRE_PASSWORD && USE_PAM
39 #include <security/pam_appl.h>
40 #include <security/pam_misc.h>
41 #endif
42
43 extern int is_local(char *);
44
45 #undef P
46 #if __STDC__
47 #define P(foo) foo
48 #else
49 #define P(foo) ()
50 #endif
51
52 typedef unsigned char boolean;
53 #define false 0
54 #define true 1
55
56 static char *whoami;
57
58 static char buf[1024];
59
60 struct finfo {
61 struct passwd *pw;
62 char *username;
63 char *full_name;
64 char *office;
65 char *office_phone;
66 char *home_phone;
67 char *other;
68 };
69
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));
79
80 extern int setpwnam P((struct passwd *pwd));
81
82 #define memzero(ptr, size) memset((char *) ptr, 0, size)
83
84 int main (argc, argv)
85 int argc;
86 char *argv[];
87 {
88 char *cp, *pwdstr;
89 uid_t uid;
90 struct finfo oldf, newf;
91 boolean interactive;
92 int status;
93 extern int errno;
94 #if REQUIRE_PASSWORD && USE_PAM
95 pam_handle_t *pamh = NULL;
96 int retcode;
97 struct pam_conv conv = { misc_conv, NULL };
98 #endif
99
100 /* whoami is the program name for error messages */
101 whoami = argv[0];
102 if (! whoami) whoami = "chfn";
103 for (cp = whoami; *cp; cp++)
104 if (*cp == '/') whoami = cp + 1;
105
106 /*
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
114 * specified for.
115 */
116 uid = getuid ();
117 memzero (&oldf, sizeof (oldf));
118 memzero (&newf, sizeof (newf));
119
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);
125 return (-1); }
126 }
127 else {
128 parse_passwd (getpwnam (newf.username), &oldf);
129 if (! oldf.username) {
130 cp = newf.username;
131 fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
132 return (-1); }
133 }
134
135 if (!(is_local(oldf.username))) {
136 fprintf (stderr, "%s: can only change local entries; use yp%s instead.\n",
137 whoami, whoami);
138 exit(1);
139 }
140
141 /* Reality check */
142 if (uid != 0 && uid != oldf.pw->pw_uid) {
143 errno = EACCES;
144 perror (whoami);
145 return (-1);
146 }
147
148 printf ("Changing finger information for %s.\n", oldf.username);
149
150 #if REQUIRE_PASSWORD
151 # if USE_PAM
152 if(uid != 0) {
153 if (pam_start("chfn", oldf.username, &conv, &pamh)) {
154 puts("Password error.");
155 exit(1);
156 }
157 if (pam_authenticate(pamh, 0)) {
158 puts("Password error.");
159 exit(1);
160 }
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.");
166 exit(1);
167 }
168 if (pam_setcred(pamh, 0)) {
169 puts("Password error.");
170 exit(1);
171 }
172 /* no need to establish a session; this isn't a session-oriented
173 * activity... */
174 }
175 # else /* USE_PAM */
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.");
182 exit(1);
183 }
184 }
185 # endif /* USE_PAM */
186 #endif /* REQUIRE_PASSWORD */
187
188
189 if (interactive) ask_info (&oldf, &newf);
190
191 if (! set_changed_data (&oldf, &newf)) {
192 printf ("Finger information not changed.\n");
193 return 0;
194 }
195 status = save_new_data (&oldf);
196 return status;
197 }
198
199 /*
200 * parse_argv () --
201 * parse the command line arguments.
202 * returns true if no information beyond the username was given.
203 */
204 static boolean parse_argv (argc, argv, pinfo)
205 int argc;
206 char *argv[];
207 struct finfo *pinfo;
208 {
209 int index, c, status;
210 boolean info_given;
211
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' },
220 };
221
222 optind = 0;
223 info_given = false;
224 while (true) {
225 c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
226 if (c == EOF) break;
227 /* version? output version and exit. */
228 if (c == 'v') {
229 printf ("%s\n", util_linux_version);
230 exit (0);
231 }
232 if (c == 'u') {
233 usage (stdout);
234 exit (0);
235 }
236 /* all other options must have an argument. */
237 if (! optarg) {
238 usage (stderr);
239 exit (-1);
240 }
241 /* ok, we were given an argument */
242 info_given = true;
243 status = 0;
244
245 strncpy (buf, whoami, sizeof(buf)-128);
246 buf[sizeof(buf)-128-1] = 0;
247 strcat (buf, ": ");
248
249 /* now store the argument */
250 switch (c) {
251 case 'f':
252 pinfo->full_name = optarg;
253 strcat (buf, "full name");
254 status = check_gecos_string (buf, optarg);
255 break;
256 case 'o':
257 pinfo->office = optarg;
258 strcat (buf, "office");
259 status = check_gecos_string (buf, optarg);
260 break;
261 case 'p':
262 pinfo->office_phone = optarg;
263 strcat (buf, "office phone");
264 status = check_gecos_string (buf, optarg);
265 break;
266 case 'h':
267 pinfo->home_phone = optarg;
268 strcat (buf, "home phone");
269 status = check_gecos_string (buf, optarg);
270 break;
271 default:
272 usage (stderr);
273 status = (-1);
274 }
275 if (status < 0) exit (status);
276 }
277 /* done parsing arguments. check for a username. */
278 if (optind < argc) {
279 if (optind + 1 < argc) {
280 usage (stderr);
281 exit (-1);
282 }
283 pinfo->username = argv[optind];
284 }
285 return (! info_given);
286 }
287
288 /*
289 * usage () --
290 * print out a usage message.
291 */
292 static void usage (fp)
293 FILE *fp;
294 {
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");
298 }
299
300 /*
301 * parse_passwd () --
302 * take a struct password and fill in the fields of the
303 * struct finfo.
304 */
305 static void parse_passwd (pw, pinfo)
306 struct passwd *pw;
307 struct finfo *pinfo;
308 {
309 char *cp;
310
311 if (pw) {
312 pinfo->pw = pw;
313 pinfo->username = pw->pw_name;
314 /* use pw_gecos */
315 cp = pw->pw_gecos;
316 pinfo->full_name = cp;
317 cp = strchr (cp, ',');
318 if (cp) { *cp = 0, cp++; } else return;
319 pinfo->office = cp;
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;
330 pinfo->other = cp;
331 }
332 }
333
334 /*
335 * ask_info () --
336 * prompt the user for the finger information and store it.
337 */
338 static void ask_info (oldfp, newfp)
339 struct finfo *oldfp;
340 struct finfo *newfp;
341 {
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);
346 printf ("\n");
347 }
348
349 /*
350 * prompt () --
351 * ask the user for a given field and check that the string is legal.
352 */
353 static char *prompt (question, def_val)
354 char *question;
355 char *def_val;
356 {
357 static char *blank = "none";
358 int len;
359 char *ans, *cp;
360
361 while (true) {
362 if (! def_val) def_val = "";
363 printf("%s [%s]: ", question, def_val);
364 *buf = 0;
365 if (fgets (buf, sizeof (buf), stdin) == NULL) {
366 printf ("\nAborted.\n");
367 exit (-1);
368 }
369 /* remove the newline at the end of buf. */
370 ans = buf;
371 while (isspace (*ans)) ans++;
372 len = strlen (ans);
373 while (len > 0 && isspace (ans[len-1])) len--;
374 if (len <= 0) return NULL;
375 ans[len] = 0;
376 if (! strcasecmp (ans, blank)) return "";
377 if (check_gecos_string (NULL, ans) >= 0) break;
378 }
379 cp = (char *) xmalloc (len + 1);
380 strcpy (cp, ans);
381 return cp;
382 }
383
384 /*
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
388 * return (-1).
389 */
390 static int check_gecos_string (msg, gecos)
391 char *msg;
392 char *gecos;
393 {
394 int i, c;
395
396 for (i = 0; i < strlen (gecos); i++) {
397 c = gecos[i];
398 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
399 if (msg) printf ("%s: ", msg);
400 printf ("'%c' is not allowed.\n", c);
401 return (-1);
402 }
403 if (iscntrl (c)) {
404 if (msg) printf ("%s: ", msg);
405 printf ("Control characters are not allowed.\n");
406 return (-1);
407 }
408 }
409 return (0);
410 }
411
412 /*
413 * set_changed_data () --
414 * incorporate the new data into the old finger info.
415 */
416 static boolean set_changed_data (oldfp, newfp)
417 struct finfo *oldfp;
418 struct finfo *newfp;
419 {
420 boolean changed = false;
421
422 if (newfp->full_name) {
423 oldfp->full_name = newfp->full_name; changed = true; }
424 if (newfp->office) {
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; }
430
431 return changed;
432 }
433
434 /*
435 * save_new_data () --
436 * save the given finger info in /etc/passwd.
437 * return zero on success.
438 */
439 static int save_new_data (pinfo)
440 struct finfo *pinfo;
441 {
442 char *gecos;
443 int len;
444
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 = "";
451
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);
459
460 /* remove trailing empty fields (but not subfields of pinfo->other) */
461 if (! pinfo->other[0] ) {
462 while (len > 0 && gecos[len-1] == ',') len--;
463 gecos[len] = 0;
464 }
465
466 /* write the new struct passwd to the passwd file. */
467 pinfo->pw->pw_gecos = gecos;
468 if (setpwnam (pinfo->pw) < 0) {
469 perror ("setpwnam");
470 printf( "Finger information *NOT* changed. Try again later.\n" );
471 return (-1);
472 }
473 printf ("Finger information changed.\n");
474 return 0;
475 }
476
477 /*
478 * xmalloc () -- malloc that never fails.
479 */
480 static void *xmalloc (bytes)
481 int bytes;
482 {
483 void *vp;
484
485 vp = malloc (bytes);
486 if (! vp && bytes > 0) {
487 perror ("malloc failed");
488 exit (-1);
489 }
490 return vp;
491 }