Upload Tizen:Base source
[framework/base/util-linux-ng.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 remove trailing empty fields.  Oct 5, 96.
18  *
19  *  1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
20  *  - added Native Language Support
21  *    
22  *
23  */
24
25 #define _BSD_SOURCE           /* for strcasecmp() */
26
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <pwd.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <getopt.h>
36 #include "my_crypt.h"
37 #include "islocal.h"
38 #include "setpwnam.h"
39 #include "xstrncpy.h"
40 #include "nls.h"
41 #include "env.h"
42
43 #ifdef HAVE_LIBSELINUX
44 #include <selinux/selinux.h>
45 #include <selinux/av_permissions.h>
46 #include "selinux_utils.h"
47 #endif
48
49 #if defined(REQUIRE_PASSWORD) && defined(HAVE_SECURITY_PAM_MISC_H)
50 #include <security/pam_appl.h>
51 #include <security/pam_misc.h>
52
53 #define PAM_FAIL_CHECK(_ph, _rc) \
54     do { \
55         if ((_rc) != PAM_SUCCESS) { \
56             fprintf(stderr, "\n%s\n", pam_strerror((_ph), (_rc))); \
57             pam_end((_ph), (_rc)); \
58             exit(1); \
59         } \
60     } while(0)
61
62 #endif
63
64 typedef unsigned char boolean;
65 #define false 0
66 #define true 1
67
68 static char *whoami;
69
70 static char buf[1024];
71
72 struct finfo {
73     struct passwd *pw;
74     char *username;
75     char *full_name;
76     char *office;
77     char *office_phone;
78     char *home_phone;
79     char *other;
80 };
81
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);
91
92 #define memzero(ptr, size) memset((char *) ptr, 0, size)
93
94 /* we do not accept gecos field sizes longer than MAX_FIELD_SIZE */
95 #define MAX_FIELD_SIZE          256
96
97 int main (int argc, char **argv) {
98     char *cp;
99     uid_t uid;
100     struct finfo oldf, newf;
101     boolean interactive;
102     int status;
103
104     sanitize_env();
105     setlocale(LC_ALL, "");      /* both for messages and for iscntrl() below */
106     bindtextdomain(PACKAGE, LOCALEDIR);
107     textdomain(PACKAGE);
108
109     /* whoami is the program name for error messages */
110     whoami = argv[0];
111     if (! whoami) whoami = "chfn";
112     for (cp = whoami; *cp; cp++)
113         if (*cp == '/') whoami = cp + 1;
114
115     /*
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
123      *  specified for.
124      */
125     uid = getuid ();
126     memzero (&oldf, sizeof (oldf));
127     memzero (&newf, sizeof (newf));
128
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);
134             return (-1); }
135     }
136     else {
137         parse_passwd (getpwnam (newf.username), &oldf);
138         if (! oldf.username) {
139             cp = newf.username;
140             fprintf (stderr, _("%s: user \"%s\" does not exist.\n"), whoami, cp);
141             return (-1); }
142     }
143
144     if (!(is_local(oldf.username))) {
145        fprintf (stderr, _("%s: can only change local entries; use yp%s instead.\n"),
146            whoami, whoami);
147        exit(1);
148     }
149
150 #ifdef HAVE_LIBSELINUX
151     if (is_selinux_enabled() > 0) {
152       if(uid == 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);
160           exit(1);
161         }
162       }
163       if (setupDefaultContext("/etc/passwd") != 0) {
164         fprintf(stderr,_("%s: Can't set default context for /etc/passwd"),
165                 whoami);
166         exit(1);
167       }
168     }
169 #endif
170
171     /* Reality check */
172     if (uid != 0 && uid != oldf.pw->pw_uid) {
173         errno = EACCES;
174         perror (whoami);
175         return (-1);
176     }
177
178     printf (_("Changing finger information for %s.\n"), oldf.username);
179
180 #ifdef REQUIRE_PASSWORD
181 #ifdef HAVE_SECURITY_PAM_MISC_H
182     if(uid != 0) {
183         pam_handle_t *pamh = NULL;
184         struct pam_conv conv = { misc_conv, NULL };
185         int retcode;
186
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));
191             exit(1);
192         }
193
194         retcode = pam_authenticate(pamh, 0);
195         PAM_FAIL_CHECK(pamh, retcode);
196
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);
201
202         retcode = pam_setcred(pamh, 0);
203         PAM_FAIL_CHECK(pamh, retcode);
204
205         pam_end(pamh, 0);
206         /* no need to establish a session; this isn't a session-oriented
207          * activity... */
208     }
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."));
216             exit(1);
217         }
218     }
219 # endif /* HAVE_SECURITY_PAM_MISC_H */
220 #endif /* REQUIRE_PASSWORD */
221
222
223     if (interactive) ask_info (&oldf, &newf);
224
225     if (! set_changed_data (&oldf, &newf)) {
226         printf (_("Finger information not changed.\n"));
227         return 0;
228     }
229     status = save_new_data (&oldf);
230     return status;
231 }
232
233 /*
234  *  parse_argv () --
235  *      parse the command line arguments.
236  *      returns true if no information beyond the username was given.
237  */
238 static boolean parse_argv (argc, argv, pinfo)
239     int argc;
240     char *argv[];
241     struct finfo *pinfo;
242 {
243     int index, c, status;
244     boolean info_given;
245
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' },
254     };
255
256     optind = 0;
257     info_given = false;
258     while (true) {
259         c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
260         if (c == -1) break;
261         /* version?  output version and exit. */
262         if (c == 'v') {
263             printf ("%s\n", PACKAGE_STRING);
264             exit (0);
265         }
266         if (c == 'u') {
267             usage (stdout);
268             exit (0);
269         }
270         /* all other options must have an argument. */
271         if (! optarg) {
272             usage (stderr);
273             exit (-1);
274         }
275         /* ok, we were given an argument */
276         info_given = true;
277         status = 0;
278
279         xstrncpy (buf, whoami, sizeof(buf)-128); 
280         strcat (buf, ": ");
281
282         /* now store the argument */
283         switch (c) {
284         case 'f':
285             pinfo->full_name = optarg;
286             strcat (buf, "full name");
287             status = check_gecos_string (buf, optarg);
288             break;
289         case 'o':
290             pinfo->office = optarg;
291             strcat (buf, "office");
292             status = check_gecos_string (buf, optarg);
293             break;
294         case 'p':
295             pinfo->office_phone = optarg;
296             strcat (buf, "office phone");
297             status = check_gecos_string (buf, optarg);
298             break;
299         case 'h':
300             pinfo->home_phone = optarg;
301             strcat (buf, "home phone");
302             status = check_gecos_string (buf, optarg);
303             break;
304         default:
305             usage (stderr);
306             status = (-1);
307         }
308         if (status < 0) exit (status);
309     }
310     /* done parsing arguments.  check for a username. */
311     if (optind < argc) {
312         if (optind + 1 < argc) {
313             usage (stderr);
314             exit (-1);
315         }
316         pinfo->username = argv[optind];
317     }
318     return (! info_given);
319 }
320
321 /*
322  *  usage () --
323  *      print out a usage message.
324  */
325 static void usage (fp)
326     FILE *fp;
327 {
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"));
331 }
332
333 /*
334  *  parse_passwd () --
335  *      take a struct password and fill in the fields of the
336  *      struct finfo.
337  */
338 static void parse_passwd (pw, pinfo)
339     struct passwd *pw;
340     struct finfo *pinfo;
341 {
342     char *gecos;
343     char *cp;
344
345     if (pw) {
346         pinfo->pw = pw;
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;
354         pinfo->office = cp;
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;
365         pinfo->other = cp;
366     }
367 }
368
369 /*
370  *  ask_info () --
371  *      prompt the user for the finger information and store it.
372  */
373 static void ask_info (oldfp, newfp)
374     struct finfo *oldfp;
375     struct finfo *newfp;
376 {
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);
381     printf ("\n");
382 }
383
384 /*
385  *  prompt () --
386  *      ask the user for a given field and check that the string is legal.
387  */
388 static char *prompt (question, def_val)
389     char *question;
390     char *def_val;
391 {
392     static char *blank = "none";
393     int len;
394     char *ans, *cp;
395   
396     while (true) {
397         if (! def_val) def_val = "";
398         printf("%s [%s]: ", question, def_val);
399         *buf = 0;
400         if (fgets (buf, sizeof (buf), stdin) == NULL) {
401             printf (_("\nAborted.\n"));
402             exit (-1);
403         }
404         /* remove the newline at the end of buf. */
405         ans = buf;
406         while (isspace (*ans)) ans++;
407         len = strlen (ans);
408         while (len > 0 && isspace (ans[len-1])) len--;
409         if (len <= 0) return NULL;
410         ans[len] = 0;
411         if (! strcasecmp (ans, blank)) return "";
412         if (check_gecos_string (NULL, ans) >= 0) break;
413     }
414     cp = (char *) xmalloc (len + 1);
415     strcpy (cp, ans);
416     return cp;
417 }
418
419 /*
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
423  *      return (-1).
424  */
425 static int check_gecos_string (msg, gecos)
426     char *msg;
427     char *gecos;
428 {
429     int i, c;
430
431     if (strlen(gecos) > MAX_FIELD_SIZE) {
432         if (msg != NULL)
433             printf("%s: ", msg);
434         printf(_("field is too long.\n"));
435         return -1;
436     }
437
438     for (i = 0; i < strlen (gecos); i++) {
439         c = gecos[i];
440         if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
441             if (msg) printf ("%s: ", msg);
442             printf (_("'%c' is not allowed.\n"), c);
443             return (-1);
444         }
445         if (iscntrl (c)) {
446             if (msg) printf ("%s: ", msg);
447             printf (_("Control characters are not allowed.\n"));
448             return (-1);
449         }
450     }
451     return (0);
452 }
453
454 /*
455  *  set_changed_data () --
456  *      incorporate the new data into the old finger info.
457  */
458 static boolean set_changed_data (oldfp, newfp)
459     struct finfo *oldfp;
460     struct finfo *newfp;
461 {
462     boolean changed = false;
463
464     if (newfp->full_name) {
465         oldfp->full_name = newfp->full_name; changed = true; }
466     if (newfp->office) {
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; }
472
473     return changed;
474 }
475
476 /*
477  *  save_new_data () --
478  *      save the given finger info in /etc/passwd.
479  *      return zero on success.
480  */
481 static int save_new_data (pinfo)
482      struct finfo *pinfo;
483 {
484     char *gecos;
485     int len;
486
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 = "";
493
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);
501
502     /* remove trailing empty fields (but not subfields of pinfo->other) */
503     if (! pinfo->other[0] ) {
504        while (len > 0 && gecos[len-1] == ',') len--;
505        gecos[len] = 0;
506     }
507
508     /* write the new struct passwd to the passwd file. */
509     pinfo->pw->pw_gecos = gecos;
510     if (setpwnam (pinfo->pw) < 0) {
511         perror ("setpwnam");
512         printf( _("Finger information *NOT* changed.  Try again later.\n" ));
513         return (-1);
514     }
515     printf (_("Finger information changed.\n"));
516     return 0;
517 }
518
519 /*
520  *  xmalloc () -- malloc that never fails.
521  */
522 static void *xmalloc (bytes)
523     int bytes;
524 {
525     void *vp;
526
527     vp = malloc (bytes);
528     if (! vp && bytes > 0) {
529         perror (_("malloc failed"));
530         exit (-1);
531     }
532     return vp;
533 }