]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/chfn.c
Imported from util-linux-2.5 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: faith $
10 * $Revision: 1.8 $
11 * $Date: 1995/10/12 14:46:35 $
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 */
17
18 static char rcsId[] = "$Version: $Id: chfn.c,v 1.8 1995/10/12 14:46:35 faith Exp $ $";
19
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <getopt.h>
29
30 #undef P
31 #if __STDC__
32 #define P(foo) foo
33 #else
34 #define P(foo) ()
35 #endif
36
37 typedef unsigned char boolean;
38 #define false 0
39 #define true 1
40
41 static char *version_string = "chfn 0.9a beta";
42 static char *whoami;
43
44 static char buf[1024];
45
46 struct finfo {
47 struct passwd *pw;
48 char *username;
49 char *full_name;
50 char *office;
51 char *office_phone;
52 char *home_phone;
53 char *other;
54 };
55
56 static boolean parse_argv P((int argc, char *argv[], struct finfo *pinfo));
57 static void usage P((FILE *fp));
58 static void parse_passwd P((struct passwd *pw, struct finfo *pinfo));
59 static void ask_info P((struct finfo *oldfp, struct finfo *newfp));
60 static char *prompt P((char *question, char *def_val));
61 static int check_gecos_string P((char *msg, char *gecos));
62 static boolean set_changed_data P((struct finfo *oldfp, struct finfo *newfp));
63 static int save_new_data P((struct finfo *pinfo));
64 static void *xmalloc P((int bytes));
65 #if 0
66 extern int strcasecmp P((char *, char *));
67 extern int setpwnam P((struct passwd *pwd));
68 #endif
69 #define memzero(ptr, size) memset((char *) ptr, 0, size)
70
71 int main (argc, argv)
72 int argc;
73 char *argv[];
74 {
75 char *cp, *pwdstr;
76 uid_t uid;
77 struct finfo oldf, newf;
78 boolean interactive;
79 int status;
80 extern int errno;
81
82 /* whoami is the program name for error messages */
83 whoami = argv[0];
84 if (! whoami) whoami = "chfn";
85 for (cp = whoami; *cp; cp++)
86 if (*cp == '/') whoami = cp + 1;
87
88 /*
89 * "oldf" contains the users original finger information.
90 * "newf" contains the changed finger information, and contains NULL
91 * in fields that haven't been changed.
92 * in the end, "newf" is folded into "oldf".
93 * the reason the new finger information is not put _immediately_ into
94 * "oldf" is that on the command line, new finger information can
95 * be specified before we know what user the information is being
96 * specified for.
97 */
98 uid = getuid ();
99 memzero (&oldf, sizeof (oldf));
100 memzero (&newf, sizeof (newf));
101
102 interactive = parse_argv (argc, argv, &newf);
103 if (! newf.username) {
104 parse_passwd (getpwuid (uid), &oldf);
105 if (! oldf.username) {
106 fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid);
107 return (-1); }
108 }
109 else {
110 parse_passwd (getpwnam (newf.username), &oldf);
111 if (! oldf.username) {
112 cp = newf.username;
113 fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
114 return (-1); }
115 }
116
117 /* reality check */
118 if (uid != 0 && uid != oldf.pw->pw_uid) {
119 errno = EACCES;
120 perror (whoami);
121 return (-1);
122 }
123
124 printf ("Changing finger information for %s.\n", oldf.username);
125
126 #if REQUIRE_PASSWORD
127 /* require password, unless root */
128 if(uid != 0 && oldf.pw->pw_passwd && oldf.pw->pw_passwd[0]) {
129 pwdstr = getpass("Password: ");
130 if(strncmp(oldf.pw->pw_passwd,
131 crypt(pwdstr, oldf.pw->pw_passwd), 13)) {
132 puts("Incorrect password.");
133 exit(1);
134 }
135 }
136 #endif
137
138
139 if (interactive) ask_info (&oldf, &newf);
140
141 if (! set_changed_data (&oldf, &newf)) {
142 printf ("Finger information not changed.\n");
143 return 0;
144 }
145 status = save_new_data (&oldf);
146 return status;
147 }
148
149 /*
150 * parse_argv () --
151 * parse the command line arguments.
152 * returns true if no information beyond the username was given.
153 */
154 static boolean parse_argv (argc, argv, pinfo)
155 int argc;
156 char *argv[];
157 struct finfo *pinfo;
158 {
159 int index, c, status;
160 boolean info_given;
161
162 static struct option long_options[] = {
163 { "full-name", required_argument, 0, 'f' },
164 { "office", required_argument, 0, 'o' },
165 { "office-phone", required_argument, 0, 'p' },
166 { "home-phone", required_argument, 0, 'h' },
167 { "help", no_argument, 0, 'u' },
168 { "version", no_argument, 0, 'v' },
169 { NULL, no_argument, 0, '0' },
170 };
171
172 optind = 0;
173 info_given = false;
174 while (true) {
175 c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
176 if (c == EOF) break;
177 /* version? output version and exit. */
178 if (c == 'v') {
179 printf ("%s\n", version_string);
180 exit (0);
181 }
182 if (c == 'u') {
183 usage (stdout);
184 exit (0);
185 }
186 /* all other options must have an argument. */
187 if (! optarg) {
188 usage (stderr);
189 exit (-1);
190 }
191 /* ok, we were given an argument */
192 info_given = true;
193 status = 0;
194 strcpy (buf, whoami); strcat (buf, ": ");
195
196 /* now store the argument */
197 switch (c) {
198 case 'f':
199 pinfo->full_name = optarg;
200 strcat (buf, "full name");
201 status = check_gecos_string (buf, optarg);
202 break;
203 case 'o':
204 pinfo->office = optarg;
205 strcat (buf, "office");
206 status = check_gecos_string (buf, optarg);
207 break;
208 case 'p':
209 pinfo->office_phone = optarg;
210 strcat (buf, "office phone");
211 status = check_gecos_string (buf, optarg);
212 break;
213 case 'h':
214 pinfo->home_phone = optarg;
215 strcat (buf, "home phone");
216 status = check_gecos_string (buf, optarg);
217 break;
218 default:
219 usage (stderr);
220 status = (-1);
221 }
222 if (status < 0) exit (status);
223 }
224 /* done parsing arguments. check for a username. */
225 if (optind < argc) {
226 if (optind + 1 < argc) {
227 usage (stderr);
228 exit (-1);
229 }
230 pinfo->username = argv[optind];
231 }
232 return (! info_given);
233 }
234
235 /*
236 * usage () --
237 * print out a usage message.
238 */
239 static void usage (fp)
240 FILE *fp;
241 {
242 fprintf (fp, "Usage: %s [ -f full-name ] [ -o office ] ", whoami);
243 fprintf (fp, "[ -p office-phone ]\n [ -h home-phone ] ");
244 fprintf (fp, "[ --help ] [ --version ]\n");
245 }
246
247 /*
248 * parse_passwd () --
249 * take a struct password and fill in the fields of the
250 * struct finfo.
251 */
252 static void parse_passwd (pw, pinfo)
253 struct passwd *pw;
254 struct finfo *pinfo;
255 {
256 char *cp;
257
258 if (pw) {
259 pinfo->pw = pw;
260 pinfo->username = pw->pw_name;
261 /* use pw_gecos */
262 cp = pw->pw_gecos;
263 pinfo->full_name = cp;
264 cp = strchr (cp, ',');
265 if (cp) { *cp = 0, cp++; } else return;
266 pinfo->office = cp;
267 cp = strchr (cp, ',');
268 if (cp) { *cp = 0, cp++; } else return;
269 pinfo->office_phone = cp;
270 cp = strchr (cp, ',');
271 if (cp) { *cp = 0, cp++; } else return;
272 pinfo->home_phone = cp;
273 /* extra fields contain site-specific information, and
274 * can not be changed by this version of chfn. */
275 cp = strchr (cp, ',');
276 if (cp) { *cp = 0, cp++; } else return;
277 pinfo->other = cp;
278 }
279 }
280
281 /*
282 * ask_info () --
283 * prompt the user for the finger information and store it.
284 */
285 static void ask_info (oldfp, newfp)
286 struct finfo *oldfp;
287 struct finfo *newfp;
288 {
289 newfp->full_name = prompt ("Name", oldfp->full_name);
290 newfp->office = prompt ("Office", oldfp->office);
291 newfp->office_phone = prompt ("Office Phone", oldfp->office_phone);
292 newfp->home_phone = prompt ("Home Phone", oldfp->home_phone);
293 printf ("\n");
294 }
295
296 /*
297 * prompt () --
298 * ask the user for a given field and check that the string is legal.
299 */
300 static char *prompt (question, def_val)
301 char *question;
302 char *def_val;
303 {
304 static char *blank = "none";
305 int len;
306 char *ans, *cp;
307
308 while (true) {
309 if (! def_val) def_val = "";
310 printf("%s [%s]: ", question, def_val);
311 *buf = 0;
312 if (fgets (buf, sizeof (buf), stdin) == NULL) {
313 printf ("\nAborted.\n");
314 exit (-1);
315 }
316 /* remove the newline at the end of buf. */
317 ans = buf;
318 while (isspace (*ans)) ans++;
319 len = strlen (ans);
320 while (len > 0 && isspace (ans[len-1])) len--;
321 if (len <= 0) return NULL;
322 ans[len] = 0;
323 if (! strcasecmp (ans, blank)) return "";
324 if (check_gecos_string (NULL, ans) >= 0) break;
325 }
326 cp = (char *) xmalloc (len + 1);
327 strcpy (cp, ans);
328 return cp;
329 }
330
331 /*
332 * check_gecos_string () --
333 * check that the given gecos string is legal. if it's not legal,
334 * output "msg" followed by a description of the problem, and
335 * return (-1).
336 */
337 static int check_gecos_string (msg, gecos)
338 char *msg;
339 char *gecos;
340 {
341 int i, c;
342
343 for (i = 0; i < strlen (gecos); i++) {
344 c = gecos[i];
345 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
346 if (msg) printf ("%s: ", msg);
347 printf ("'%c' is not allowed.\n", c);
348 return (-1);
349 }
350 if (iscntrl (c)) {
351 if (msg) printf ("%s: ", msg);
352 printf ("Control characters are not allowed.\n");
353 return (-1);
354 }
355 }
356 return (0);
357 }
358
359 /*
360 * set_changed_data () --
361 * incorporate the new data into the old finger info.
362 */
363 static boolean set_changed_data (oldfp, newfp)
364 struct finfo *oldfp;
365 struct finfo *newfp;
366 {
367 boolean changed = false;
368
369 if (newfp->full_name) {
370 oldfp->full_name = newfp->full_name; changed = true; }
371 if (newfp->office) {
372 oldfp->office = newfp->office; changed = true; }
373 if (newfp->office_phone) {
374 oldfp->office_phone = newfp->office_phone; changed = true; }
375 if (newfp->home_phone) {
376 oldfp->home_phone = newfp->home_phone; changed = true; }
377
378 return changed;
379 }
380
381 /*
382 * save_new_data () --
383 * save the given finger info in /etc/passwd.
384 * return zero on success.
385 */
386 static int save_new_data (pinfo)
387 struct finfo *pinfo;
388 {
389 char *gecos;
390 int len;
391
392 /* null fields will confuse printf(). */
393 if (! pinfo->full_name) pinfo->full_name = "";
394 if (! pinfo->office) pinfo->office = "";
395 if (! pinfo->office_phone) pinfo->office_phone = "";
396 if (! pinfo->home_phone) pinfo->home_phone = "";
397 if (! pinfo->other) pinfo->other = "";
398
399 /* create the new gecos string */
400 len = (strlen (pinfo->full_name) + strlen (pinfo->office) +
401 strlen (pinfo->office_phone) + strlen (pinfo->home_phone) +
402 strlen (pinfo->other) + 4);
403 gecos = (char *) xmalloc (len + 1);
404 sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
405 pinfo->office_phone, pinfo->home_phone, pinfo->other);
406
407 /* write the new struct passwd to the passwd file. */
408 pinfo->pw->pw_gecos = gecos;
409 if (setpwnam (pinfo->pw) < 0) {
410 perror ("setpwnam");
411 printf( "Finger information *NOT* changed. Try again later.\n" );
412 return (-1);
413 }
414 printf ("Finger information changed.\n");
415 return 0;
416 }
417
418 /*
419 * xmalloc () -- malloc that never fails.
420 */
421 static void *xmalloc (bytes)
422 int bytes;
423 {
424 void *vp;
425
426 vp = malloc (bytes);
427 if (! vp && bytes > 0) {
428 perror ("malloc failed");
429 exit (-1);
430 }
431 return vp;
432 }