Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (8 years ago)
Author:
bruno
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
Location:
branches/2.2.9/mindi-busybox/loginutils
Files:
3 added
13 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/loginutils/Config.in

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Config.src
    12#
    23# For a description of the syntax of this configuration file,
     
    67menu "Login/Password Management Utilities"
    78
     9config ADD_SHELL
     10       bool "add-shell"
     11       default y if DESKTOP
     12       help
     13         Add shells to /etc/shells.
     14
     15config REMOVE_SHELL
     16       bool "remove-shell"
     17       default y if DESKTOP
     18       help
     19         Remove shells from /etc/shells.
     20
    821config FEATURE_SHADOWPASSWDS
    922    bool "Support for shadow passwords"
    10     default n
    11     help
    12       Build support for shadow password in /etc/shadow.  This file is only
     23    default y
     24    help
     25      Build support for shadow password in /etc/shadow. This file is only
    1326      readable by root and thus the encrypted passwords are no longer
    1427      publicly readable.
    1528
    16 config USE_BB_SHADOW
    17     bool "  Use busybox shadow password functions"
    18     default y
    19     depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS
    20     help
    21         If you leave this disabled, busybox will use the system's shadow
    22         password handling functions.  And if you are using the GNU C library
    23         (glibc), you will then need to install the /etc/nsswitch.conf
    24         configuration file and the required /lib/libnss_* libraries in
    25         order for the shadow password functions to work.  This generally
    26         makes your embedded system quite a bit larger.
    27 
    28         Enabling this option will cause busybox to directly access the
    29         system's /etc/shadow file when handling shadow passwords.  This
    30         makes your system smaller and I will get fewer emails asking about
    31         how glibc NSS works).  When this option is enabled, you will not be
    32         able to use PAM to access shadow passwords from remote LDAP
    33         password servers and whatnot.
    34 
    3529config USE_BB_PWD_GRP
    3630    bool "Use internal password and group functions rather than system functions"
     31    default y
     32    help
     33      If you leave this disabled, busybox will use the system's password
     34      and group functions. And if you are using the GNU C library
     35      (glibc), you will then need to install the /etc/nsswitch.conf
     36      configuration file and the required /lib/libnss_* libraries in
     37      order for the password and group functions to work. This generally
     38      makes your embedded system quite a bit larger.
     39
     40      Enabling this option will cause busybox to directly access the
     41      system's /etc/password, /etc/group files (and your system will be
     42      smaller, and I will get fewer emails asking about how glibc NSS
     43      works). When this option is enabled, you will not be able to use
     44      PAM to access remote LDAP password servers and whatnot. And if you
     45      want hostname resolution to work with glibc, you still need the
     46      /lib/libnss_* libraries.
     47
     48      If you need to use glibc's nsswitch.conf mechanism
     49      (e.g. if user/group database is NOT stored in /etc/passwd etc),
     50      you must NOT use this option.
     51
     52      If you enable this option, it will add about 1.5k.
     53
     54config USE_BB_SHADOW
     55    bool "Use internal shadow password functions"
     56    default y
     57    depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS
     58    help
     59      If you leave this disabled, busybox will use the system's shadow
     60      password handling functions. And if you are using the GNU C library
     61      (glibc), you will then need to install the /etc/nsswitch.conf
     62      configuration file and the required /lib/libnss_* libraries in
     63      order for the shadow password functions to work. This generally
     64      makes your embedded system quite a bit larger.
     65
     66      Enabling this option will cause busybox to directly access the
     67      system's /etc/shadow file when handling shadow passwords. This
     68      makes your system smaller (and I will get fewer emails asking about
     69      how glibc NSS works). When this option is enabled, you will not be
     70      able to use PAM to access shadow passwords from remote LDAP
     71      password servers and whatnot.
     72
     73config USE_BB_CRYPT
     74    bool "Use internal crypt functions"
     75    default y
     76    help
     77      Busybox has internal DES and MD5 crypt functions.
     78      They produce results which are identical to corresponding
     79      standard C library functions.
     80
     81      If you leave this disabled, busybox will use the system's
     82      crypt functions. Most C libraries use large (~70k)
     83      static buffers there, and also combine them with more general
     84      DES encryption/decryption.
     85
     86      For busybox, having large static buffers is undesirable,
     87      especially on NOMMU machines. Busybox also doesn't need
     88      DES encryption/decryption and can do with smaller code.
     89
     90      If you enable this option, it will add about 4.8k of code
     91      if you are building dynamically linked executable.
     92      In static build, it makes code _smaller_ by about 1.2k,
     93      and likely many kilobytes less of bss.
     94
     95config USE_BB_CRYPT_SHA
     96    bool "Enable SHA256/512 crypt functions"
     97    default y
     98    depends on USE_BB_CRYPT
     99    help
     100      Enable this if you have passwords starting with "$5$" or "$6$"
     101      in your /etc/passwd or /etc/shadow files. These passwords
     102      are hashed using SHA256 and SHA512 algorithms. Support for them
     103      was added to glibc in 2008.
     104      With this option off, login will fail password check for any
     105      user which has password encrypted with these algorithms.
     106
     107config ADDUSER
     108    bool "adduser"
     109    default y
     110    help
     111      Utility for creating a new user account.
     112
     113config FEATURE_ADDUSER_LONG_OPTIONS
     114    bool "Enable long options"
     115    default y
     116    depends on ADDUSER && LONG_OPTS
     117    help
     118      Support long options for the adduser applet.
     119
     120config FEATURE_CHECK_NAMES
     121    bool "Enable sanity check on user/group names in adduser and addgroup"
    37122    default n
    38     help
    39         If you leave this disabled, busybox will use the system's password
    40         and group functions.  And if you are using the GNU C library
    41         (glibc), you will then need to install the /etc/nsswitch.conf
    42         configuration file and the required /lib/libnss_* libraries in
    43         order for the password and group functions to work.  This generally
    44         makes your embedded system quite a bit larger.
    45 
    46         Enabling this option will cause busybox to directly access the
    47         system's /etc/password, /etc/group files (and your system will be
    48         smaller, and I will get fewer emails asking about how glibc NSS
    49         works).  When this option is enabled, you will not be able to use
    50         PAM to access remote LDAP password servers and whatnot.  And if you
    51         want hostname resolution to work with glibc, you still need the
    52         /lib/libnss_* libraries.
    53 
    54         If you enable this option, it will add about 1.5k to busybox.
     123    depends on ADDUSER || ADDGROUP
     124    help
     125      Enable sanity check on user and group names in adduser and addgroup.
     126      To avoid problems, the user or group name should consist only of
     127      letters, digits, underscores, periods, at signs and dashes,
     128      and not start with a dash (as defined by IEEE Std 1003.1-2001).
     129      For compatibility with Samba machine accounts "$" is also supported
     130      at the end of the user or group name.
     131
     132config FIRST_SYSTEM_ID
     133    int "First valid system uid or gid for adduser and addgroup"
     134    depends on ADDUSER || ADDGROUP
     135    range 0 64900
     136    default 100
     137    help
     138      First valid system uid or gid for adduser and addgroup
     139
     140config LAST_SYSTEM_ID
     141    int "Last valid system uid or gid for adduser and addgroup"
     142    depends on ADDUSER || ADDGROUP
     143    range 0 64900
     144    default 999
     145    help
     146      Last valid system uid or gid for adduser and addgroup
    55147
    56148config ADDGROUP
    57149    bool "addgroup"
    58     default n
     150    default y
    59151    help
    60152      Utility for creating a new group account.
     153
     154config FEATURE_ADDGROUP_LONG_OPTIONS
     155    bool "Enable long options"
     156    default y
     157    depends on ADDGROUP && LONG_OPTS
     158    help
     159      Support long options for the addgroup applet.
    61160
    62161config FEATURE_ADDUSER_TO_GROUP
    63162    bool "Support for adding users to groups"
    64     default n
     163    default y
    65164    depends on ADDGROUP
    66165    help
     
    69168      existing group.
    70169
     170config DELUSER
     171    bool "deluser"
     172    default y
     173    help
     174      Utility for deleting a user account.
     175
    71176config DELGROUP
    72177    bool "delgroup"
    73     default n
     178    default y
    74179    help
    75180      Utility for deleting a group account.
    76181
    77182config FEATURE_DEL_USER_FROM_GROUP
    78     bool "Support for removing users from groups."
    79     default n
     183    bool "Support for removing users from groups"
     184    default y
    80185    depends on DELGROUP
    81186    help
     
    83188      or delgroup will remove an user from a specified group.
    84189
    85 config ADDUSER
    86     bool "adduser"
    87     default n
    88     help
    89       Utility for creating a new user account.
    90 
    91 config DELUSER
    92     bool "deluser"
    93     default n
    94     help
    95       Utility for deleting a user account.
    96 
    97190config GETTY
    98191    bool "getty"
    99     default n
     192    default y
    100193    select FEATURE_SYSLOG
    101194    help
    102195      getty lets you log in on a tty, it is normally invoked by init.
    103 
    104 config FEATURE_UTMP
    105     bool "Support utmp file"
    106     depends on GETTY || LOGIN || SU || WHO
    107     default n
    108     help
    109       The file /var/run/utmp is used to track who is currently logged in.
    110 
    111 config FEATURE_WTMP
    112     bool "Support wtmp file"
    113     depends on GETTY || LOGIN || SU || LAST
    114     default n
    115     select FEATURE_UTMP
    116     help
    117       The file /var/run/wtmp is used to track when user's have logged into
    118       and logged out of the system.
    119196
    120197config LOGIN
    121198    bool "login"
    122     default n
    123     select FEATURE_SUID
     199    default y
    124200    select FEATURE_SYSLOG
    125201    help
     
    139215    bool "Support for login scripts"
    140216    depends on LOGIN
    141     default n
     217    default y
    142218    help
    143219      Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
     
    163239config PASSWD
    164240    bool "passwd"
    165     default n
    166     select FEATURE_SUID
    167     select FEATURE_SYSLOG
    168     help
    169       passwd changes passwords for user and group accounts.  A normal user
     241    default y
     242    select FEATURE_SYSLOG
     243    help
     244      passwd changes passwords for user and group accounts. A normal user
    170245      may only change the password for his/her own account, the super user
    171       may change the password for any account.  The administrator of a group
     246      may change the password for any account. The administrator of a group
    172247      may change the password for the group.
    173248
     
    184259config CRYPTPW
    185260    bool "cryptpw"
    186     default n
    187     help
    188       Applet for crypting a string.
     261    default y
     262    help
     263      Encrypts the given password with the crypt(3) libc function
     264      using the given salt. Debian has this utility under mkpasswd
     265      name. Busybox provides mkpasswd as an alias for cryptpw.
    189266
    190267config CHPASSWD
    191        bool "chpasswd"
    192        default n
    193        help
    194          chpasswd  reads  a  file  of user name and password pairs from
    195          standard input and uses this information to update a group of
    196          existing users.
     268    bool "chpasswd"
     269    default y
     270    help
     271      Reads a file of user name and password pairs from standard input
     272      and uses this information to update a group of existing users.
    197273
    198274config SU
    199275    bool "su"
    200     default n
    201     select FEATURE_SUID
     276    default y
    202277    select FEATURE_SYSLOG
    203278    help
     
    220295config SULOGIN
    221296    bool "sulogin"
    222     default n
     297    default y
    223298    select FEATURE_SYSLOG
    224299    help
     
    228303config VLOCK
    229304    bool "vlock"
    230     default n
    231     select FEATURE_SUID
     305    default y
    232306    help
    233307      Build the "vlock" applet which allows you to lock (virtual) terminals.
     
    237311
    238312endmenu
    239 
  • branches/2.2.9/mindi-busybox/loginutils/Kbuild

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Kbuild.src
    12# Makefile for busybox
    23#
    34# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
    45#
    5 # Licensed under the GPL v2, see the file LICENSE in this tarball.
     6# Licensed under GPLv2, see file LICENSE in this source tree.
    67
    78lib-y:=
     9
     10lib-$(CONFIG_ADD_SHELL)    += add-remove-shell.o
     11lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
    812lib-$(CONFIG_ADDGROUP)  += addgroup.o
    913lib-$(CONFIG_ADDUSER)   += adduser.o
  • branches/2.2.9/mindi-busybox/loginutils/addgroup.c

    r1765 r2725  
    77 * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
    88 *
    9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1010 *
    1111 */
    12 
    1312#include "libbb.h"
    1413
     14#if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
     15#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
     16#endif
     17
     18#define OPT_GID                       (1 << 0)
     19#define OPT_SYSTEM_ACCOUNT            (1 << 1)
     20
     21/* We assume GID_T_MAX == INT_MAX */
    1522static void xgroup_study(struct group *g)
    1623{
     24    unsigned max = INT_MAX;
     25
    1726    /* Make sure gr_name is unused */
    1827    if (getgrnam(g->gr_name)) {
    19         goto error;
     28        bb_error_msg_and_die("%s '%s' in use", "group", g->gr_name);
     29        /* these format strings are reused in adduser and addgroup */
    2030    }
    2131
     32    /* if a specific gid is requested, the --system switch and */
     33    /* min and max values are overridden, and the range of valid */
     34    /* gid values is set to [0, INT_MAX] */
     35    if (!(option_mask32 & OPT_GID)) {
     36        if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
     37            g->gr_gid = CONFIG_FIRST_SYSTEM_ID;
     38            max = CONFIG_LAST_SYSTEM_ID;
     39        } else {
     40            g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1;
     41            max = 64999;
     42        }
     43    }
    2244    /* Check if the desired gid is free
    2345     * or find the first free one */
     
    2648            return; /* found free group: return */
    2749        }
    28         if (option_mask32) {
     50        if (option_mask32 & OPT_GID) {
    2951            /* -g N, cannot pick gid other than N: error */
    30             g->gr_name = itoa(g->gr_gid);
    31             goto error;
     52            bb_error_msg_and_die("%s '%s' in use", "gid", itoa(g->gr_gid));
     53            /* this format strings is reused in adduser and addgroup */
     54        }
     55        if (g->gr_gid == max) {
     56            /* overflowed: error */
     57            bb_error_msg_and_die("no %cids left", 'g');
     58            /* this format string is reused in adduser and addgroup */
    3259        }
    3360        g->gr_gid++;
    34         if (g->gr_gid <= 0) {
    35             /* overflowed: error */
    36             bb_error_msg_and_die("no gids left");
    37         }
    3861    }
    39 
    40  error:
    41     /* exit */
    42     bb_error_msg_and_die("group %s already exists", g->gr_name);
    4362}
    4463
     
    4665static void new_group(char *group, gid_t gid)
    4766{
    48     FILE *file;
    4967    struct group gr;
     68    char *p;
    5069
    5170    /* make sure gid and group haven't already been allocated */
     
    5574
    5675    /* add entry to group */
    57     file = xfopen(bb_path_group_file, "a");
    58     /* group:passwd:gid:userlist */
    59     fprintf(file, "%s:x:%d:\n", group, gr.gr_gid);
     76    p = xasprintf("x:%u:", (unsigned) gr.gr_gid);
     77    if (update_passwd(bb_path_group_file, group, p, NULL) < 0)
     78        exit(EXIT_FAILURE);
    6079    if (ENABLE_FEATURE_CLEAN_UP)
    61         fclose(file);
     80        free(p);
    6281#if ENABLE_FEATURE_SHADOWPASSWDS
    63     file = fopen_or_warn(bb_path_gshadow_file, "a");
    64     if (file) {
    65         fprintf(file, "%s:!::\n", group);
    66         if (ENABLE_FEATURE_CLEAN_UP)
    67             fclose(file);
    68     }
     82    /* /etc/gshadow fields:
     83     * 1. Group name.
     84     * 2. Encrypted password.
     85     *    If set, non-members of the group can join the group
     86     *    by typing the password for that group using the newgrp command.
     87     *    If the value is of this field ! then no user is allowed
     88     *    to access the group using the newgrp command. A value of !!
     89     *    is treated the same as a value of ! only it indicates
     90     *    that a password has never been set before. If the value is null,
     91     *    only group members can log into the group.
     92     * 3. Group administrators (comma delimited list).
     93     *    Group members listed here can add or remove group members
     94     *    using the gpasswd command.
     95     * 4. Group members (comma delimited list).
     96     */
     97    /* Ignore errors: if file is missing we assume admin doesn't want it */
     98    update_passwd(bb_path_gshadow_file, group, "!::", NULL);
    6999#endif
    70100}
    71101
    72 #if ENABLE_FEATURE_ADDUSER_TO_GROUP
    73 static void add_user_to_group(char **args,
    74         const char *path,
    75         FILE *(*fopen_func)(const char *fileName, const char *mode))
    76 {
    77     char *line;
    78     int len = strlen(args[1]);
    79     llist_t *plist = NULL;
    80     FILE *group_file;
    81 
    82     group_file = fopen_func(path, "r");
    83 
    84     if (!group_file) return;
    85 
    86     while ((line = xmalloc_getline(group_file))) {
    87         /* Find the group */
    88         if (!strncmp(line, args[1], len)
    89          && line[len] == ':'
    90         ) {
    91             /* Add the new user */
    92             line = xasprintf("%s%s%s", line,
    93                         last_char_is(line, ':') ? "" : ",",
    94                         args[0]);
    95         }
    96         llist_add_to_end(&plist, line);
    97     }
    98 
    99     if (ENABLE_FEATURE_CLEAN_UP) {
    100         fclose(group_file);
    101         group_file = fopen_func(path, "w");
    102         while ((line = llist_pop(&plist))) {
    103             if (group_file)
    104                 fprintf(group_file, "%s\n", line);
    105             free(line);
    106         }
    107         if (group_file)
    108             fclose(group_file);
    109     } else {
    110         group_file = fopen_func(path, "w");
    111         if (group_file)
    112             while ((line = llist_pop(&plist)))
    113                 fprintf(group_file, "%s\n", line);
    114     }
    115 }
     102#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS
     103static const char addgroup_longopts[] ALIGN1 =
     104        "gid\0"                 Required_argument "g"
     105        "system\0"              No_argument       "S"
     106        ;
    116107#endif
    117108
     
    123114 * will add an existing user to an existing group.
    124115 */
    125 int addgroup_main(int argc, char **argv);
    126 int addgroup_main(int argc, char **argv)
     116int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     117int addgroup_main(int argc UNUSED_PARAM, char **argv)
    127118{
    128     char *group;
    129     gid_t gid = 0;
     119    unsigned opts;
     120    unsigned gid = 0;
    130121
    131122    /* need to be root */
     
    133124        bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
    134125    }
    135 
     126#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS
     127    applet_long_options = addgroup_longopts;
     128#endif
    136129    /* Syntax:
    137130     *  addgroup group
     
    139132     *  addgroup user group
    140133     * Check for min, max and missing args */
    141     opt_complementary = "-1:?2";
    142     if (getopt32(argv, "g:", &group)) {
    143         gid = xatoul_range(group, 0, ((unsigned long)(gid_t)ULONG_MAX) >> 1);
    144     }
     134    opt_complementary = "-1:?2:g+";
     135    opts = getopt32(argv, "g:S", &gid);
    145136    /* move past the commandline options */
    146137    argv += optind;
    147     argc -= optind;
     138    //argc -= optind;
    148139
    149140#if ENABLE_FEATURE_ADDUSER_TO_GROUP
    150     if (argc == 2) {
     141    if (argv[1]) {
    151142        struct group *gr;
    152143
    153         if (option_mask32) {
     144        if (opts & OPT_GID) {
    154145            /* -g was there, but "addgroup -g num user group"
    155146             * is a no-no */
     
    159150        /* check if group and user exist */
    160151        xuname2uid(argv[0]); /* unknown user: exit */
    161         xgroup2gid(argv[1]); /* unknown group: exit */
     152        gr = xgetgrnam(argv[1]); /* unknown group: exit */
    162153        /* check if user is already in this group */
    163         gr = getgrnam(argv[1]);
    164154        for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
    165155            if (!strcmp(argv[0], *(gr->gr_mem))) {
     
    168158            }
    169159        }
    170         add_user_to_group(argv, bb_path_group_file, xfopen);
    171 #if ENABLE_FEATURE_SHADOWPASSWDS
    172         add_user_to_group(argv, bb_path_gshadow_file, fopen_or_warn);
    173 #endif /* ENABLE_FEATURE_SHADOWPASSWDS */
     160        if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) {
     161            return EXIT_FAILURE;
     162        }
     163# if ENABLE_FEATURE_SHADOWPASSWDS
     164        update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]);
     165# endif
    174166    } else
    175167#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
     168    {
     169        die_if_bad_username(argv[0]);
    176170        new_group(argv[0], gid);
    177 
     171    }
    178172    /* Reached only on success */
    179173    return EXIT_SUCCESS;
  • branches/2.2.9/mindi-busybox/loginutils/adduser.c

    r1765 r2725  
    66 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
    77 *
    8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    99 */
    10 
    1110#include "libbb.h"
    1211
     12#if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
     13#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
     14#endif
     15
     16/* #define OPT_HOME           (1 << 0) */ /* unused */
     17/* #define OPT_GECOS          (1 << 1) */ /* unused */
     18#define OPT_SHELL          (1 << 2)
     19#define OPT_GID            (1 << 3)
    1320#define OPT_DONT_SET_PASS  (1 << 4)
     21#define OPT_SYSTEM_ACCOUNT (1 << 5)
    1422#define OPT_DONT_MAKE_HOME (1 << 6)
    15 
    16 
     23#define OPT_UID            (1 << 7)
     24
     25/* We assume UID_T_MAX == INT_MAX */
    1726/* remix */
    18 /* EDR recoded such that the uid may be passed in *p */
    19 static int passwd_study(const char *filename, struct passwd *p)
    20 {
    21     enum { min = 500, max = 65000 };
    22     FILE *passwd;
    23     /* We are using reentrant fgetpwent_r() in order to avoid
    24      * pulling in static buffers from libc (think static build here) */
    25     char buffer[256];
    26     struct passwd pw;
    27     struct passwd *result;
    28 
    29     passwd = xfopen(filename, "r");
    30 
    31     /* EDR if uid is out of bounds, set to min */
    32     if ((p->pw_uid > max) || (p->pw_uid < min))
    33         p->pw_uid = min;
    34 
    35     /* stuff to do:
    36      * make sure login isn't taken;
    37      * find free uid and gid;
    38      */
    39     while (!fgetpwent_r(passwd, &pw, buffer, sizeof(buffer), &result)) {
    40         if (strcmp(pw.pw_name, p->pw_name) == 0) {
    41             /* return 0; */
    42             return 1;
    43         }
    44         if ((pw.pw_uid >= p->pw_uid) && (pw.pw_uid < max)
    45             && (pw.pw_uid >= min)) {
    46             p->pw_uid = pw.pw_uid + 1;
    47         }
    48     }
    49 
    50     if (p->pw_gid == 0) {
    51         /* EDR check for an already existing gid */
    52         while (getgrgid(p->pw_uid) != NULL)
    53             p->pw_uid++;
    54 
    55         /* EDR also check for an existing group definition */
    56         if (getgrnam(p->pw_name) != NULL)
    57             return 3;
    58 
    59         /* EDR create new gid always = uid */
    60         p->pw_gid = p->pw_uid;
    61     }
    62 
    63     /* EDR bounds check */
    64     if ((p->pw_uid > max) || (p->pw_uid < min))
    65         return 2;
    66 
    67     /* return 1; */
    68     return 0;
    69 }
    70 
    71 static void addgroup_wrapper(struct passwd *p)
     27/* recoded such that the uid may be passed in *p */
     28static void passwd_study(struct passwd *p)
     29{
     30    int max = UINT_MAX;
     31
     32    if (getpwnam(p->pw_name)) {
     33        bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name);
     34        /* this format string is reused in adduser and addgroup */
     35    }
     36
     37    if (!(option_mask32 & OPT_UID)) {
     38        if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
     39            p->pw_uid = CONFIG_FIRST_SYSTEM_ID;
     40            max = CONFIG_LAST_SYSTEM_ID;
     41        } else {
     42            p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1;
     43            max = 64999;
     44        }
     45    }
     46    /* check for a free uid (and maybe gid) */
     47    while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) {
     48        if (option_mask32 & OPT_UID) {
     49            /* -u N, cannot pick uid other than N: error */
     50            bb_error_msg_and_die("%s '%s' in use", "uid", itoa(p->pw_uid));
     51            /* this format string is reused in adduser and addgroup */
     52        }
     53        if (p->pw_uid == max) {
     54            bb_error_msg_and_die("no %cids left", 'u');
     55        }
     56        p->pw_uid++;
     57    }
     58
     59    if (p->pw_gid == (gid_t)-1) {
     60        p->pw_gid = p->pw_uid; /* new gid = uid */
     61        if (getgrnam(p->pw_name)) {
     62            bb_error_msg_and_die("%s '%s' in use", "group", p->pw_name);
     63            /* this format string is reused in adduser and addgroup */
     64        }
     65    }
     66}
     67
     68static void addgroup_wrapper(struct passwd *p, const char *group_name)
    7269{
    7370    char *cmd;
    7471
    75     cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name);
     72    if (group_name) /* Add user to existing group */
     73        cmd = xasprintf("addgroup '%s' '%s'", p->pw_name, group_name);
     74    else    /* Add user to his own group with the first free gid found in passwd_study */
     75        cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
     76    /* Warning: to be compatible with external addgroup programs we should use --gid instead */
    7677    system(cmd);
    7778    free(cmd);
    7879}
    7980
    80 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
     81static void passwd_wrapper(const char *login) NORETURN;
    8182
    8283static void passwd_wrapper(const char *login)
    8384{
    84     static const char prog[] ALIGN1 = "passwd";
    85 
    86     BB_EXECLP(prog, prog, login, NULL);
    87     bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login);
    88 }
    89 
    90 /* putpwent(3) remix */
    91 static int adduser(struct passwd *p)
    92 {
    93     FILE *file;
    94     int addgroup = !p->pw_gid;
    95 
    96     /* make sure everything is kosher and setup uid && gid */
    97     file = xfopen(bb_path_passwd_file, "a");
    98     fseek(file, 0, SEEK_END);
    99 
    100     switch (passwd_study(bb_path_passwd_file, p)) {
    101         case 1:
    102             bb_error_msg_and_die("%s: login already in use", p->pw_name);
    103         case 2:
    104             bb_error_msg_and_die("illegal uid or no uids left");
    105         case 3:
    106             bb_error_msg_and_die("%s: group name already in use", p->pw_name);
    107     }
    108 
    109     /* add to passwd */
    110     if (putpwent(p, file) == -1) {
    111         bb_perror_nomsg_and_die();
    112     }
    113     /* Do fclose even if !ENABLE_FEATURE_CLEAN_UP.
    114      * We will exec passwd, files must be flushed & closed before that! */
    115     fclose(file);
    116 
    117 #if ENABLE_FEATURE_SHADOWPASSWDS
    118     /* add to shadow if necessary */
    119     file = fopen_or_warn(bb_path_shadow_file, "a");
    120     if (file) {
    121         fseek(file, 0, SEEK_END);
    122         fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
    123                 p->pw_name,             /* username */
    124                 time(NULL) / 86400,     /* sp->sp_lstchg */
    125                 0,                      /* sp->sp_min */
    126                 99999,                  /* sp->sp_max */
    127                 7);                     /* sp->sp_warn */
    128         fclose(file);
    129     }
    130 #endif
    131 
    132     /* add to group */
    133     /* addgroup should be responsible for dealing w/ gshadow */
    134     /* if using a pre-existing group, don't create one */
    135     if (addgroup) addgroup_wrapper(p);
    136 
    137     /* Clear the umask for this process so it doesn't
    138      * screw up the permissions on the mkdir and chown. */
    139     umask(0);
    140     if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
    141         /* Set the owner and group so it is owned by the new user,
    142            then fix up the permissions to 2755. Can't do it before
    143            since chown will clear the setgid bit */
    144         if (mkdir(p->pw_dir, 0755)
    145         || chown(p->pw_dir, p->pw_uid, p->pw_gid)
    146         || chmod(p->pw_dir, 02755)) {
    147             bb_perror_msg("%s", p->pw_dir);
    148         }
    149     }
    150 
    151     if (!(option_mask32 & OPT_DONT_SET_PASS)) {
    152         /* interactively set passwd */
    153         passwd_wrapper(p->pw_name);
    154     }
    155 
    156     return 0;
    157 }
     85    BB_EXECLP("passwd", "passwd", login, NULL);
     86    bb_error_msg_and_die("can't execute passwd, you must set password manually");
     87}
     88
     89#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
     90static const char adduser_longopts[] ALIGN1 =
     91        "home\0"                Required_argument "h"
     92        "gecos\0"               Required_argument "g"
     93        "shell\0"               Required_argument "s"
     94        "ingroup\0"             Required_argument "G"
     95        "disabled-password\0"   No_argument       "D"
     96        "empty-password\0"      No_argument       "D"
     97        "system\0"              No_argument       "S"
     98        "no-create-home\0"      No_argument       "H"
     99        "uid\0"                 Required_argument "u"
     100        ;
     101#endif
    158102
    159103/*
    160104 * adduser will take a login_name as its first parameter.
    161  *
    162  * home
    163  * shell
    164  * gecos
    165  *
     105 * home, shell, gecos:
    166106 * can be customized via command-line parameters.
    167107 */
    168 int adduser_main(int argc, char **argv);
    169 int adduser_main(int argc, char **argv)
     108int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     109int adduser_main(int argc UNUSED_PARAM, char **argv)
    170110{
    171111    struct passwd pw;
    172112    const char *usegroup = NULL;
     113    char *p;
     114    unsigned opts;
     115
     116#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
     117    applet_long_options = adduser_longopts;
     118#endif
    173119
    174120    /* got root? */
     
    182128
    183129    /* exactly one non-option arg */
    184     opt_complementary = "=1";
    185     getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
     130    /* disable interactive passwd for system accounts */
     131    opt_complementary = "=1:SD:u+";
     132    if (sizeof(pw.pw_uid) == sizeof(int)) {
     133        opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid);
     134    } else {
     135        unsigned uid;
     136        opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid);
     137        if (opts & OPT_UID) {
     138            pw.pw_uid = uid;
     139        }
     140    }
    186141    argv += optind;
    187142
    188     /* create a passwd struct */
     143    /* fill in the passwd struct */
    189144    pw.pw_name = argv[0];
     145    die_if_bad_username(pw.pw_name);
    190146    if (!pw.pw_dir) {
    191147        /* create string for $HOME if not specified already */
     
    193149    }
    194150    pw.pw_passwd = (char *)"x";
    195     pw.pw_uid = 0;
    196     pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
    197 
    198     /* grand finale */
    199     return adduser(&pw);
    200 }
     151    if (opts & OPT_SYSTEM_ACCOUNT) {
     152        if (!usegroup) {
     153            usegroup = "nogroup";
     154        }
     155        if (!(opts & OPT_SHELL)) {
     156            pw.pw_shell = (char *) "/bin/false";
     157        }
     158    }
     159    pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */
     160
     161    /* make sure everything is kosher and setup uid && maybe gid */
     162    passwd_study(&pw);
     163
     164    p = xasprintf("x:%u:%u:%s:%s:%s",
     165            (unsigned) pw.pw_uid, (unsigned) pw.pw_gid,
     166            pw.pw_gecos, pw.pw_dir, pw.pw_shell);
     167    if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) {
     168        return EXIT_FAILURE;
     169    }
     170    if (ENABLE_FEATURE_CLEAN_UP)
     171        free(p);
     172
     173#if ENABLE_FEATURE_SHADOWPASSWDS
     174    /* /etc/shadow fields:
     175     * 1. username
     176     * 2. encrypted password
     177     * 3. last password change (unix date (unix time/24*60*60))
     178     * 4. minimum days required between password changes
     179     * 5. maximum days password is valid
     180     * 6. days before password is to expire that user is warned
     181     * 7. days after password expires that account is disabled
     182     * 8. unix date when login expires (i.e. when it may no longer be used)
     183     */
     184    /* fields:     2 3  4 5     6 78 */
     185    p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL)) / (24*60*60));
     186    /* ignore errors: if file is missing we suppose admin doesn't want it */
     187    update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL);
     188    if (ENABLE_FEATURE_CLEAN_UP)
     189        free(p);
     190#endif
     191
     192    /* add to group */
     193    addgroup_wrapper(&pw, usegroup);
     194
     195    /* clear the umask for this process so it doesn't
     196     * screw up the permissions on the mkdir and chown. */
     197    umask(0);
     198    if (!(opts & OPT_DONT_MAKE_HOME)) {
     199        /* set the owner and group so it is owned by the new user,
     200         * then fix up the permissions to 2755. Can't do it before
     201         * since chown will clear the setgid bit */
     202        int mkdir_err = mkdir(pw.pw_dir, 0755);
     203        if (mkdir_err == 0) {
     204            /* New home. Copy /etc/skel to it */
     205            const char *args[] = {
     206                "chown", "-R",
     207                xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid),
     208                pw.pw_dir, NULL
     209            };
     210            /* Be silent on any errors (like: no /etc/skel) */
     211            logmode = LOGMODE_NONE;
     212            copy_file("/etc/skel", pw.pw_dir, FILEUTILS_RECUR);
     213            logmode = LOGMODE_STDIO;
     214            chown_main(4, (char**)args);
     215        }
     216        if ((mkdir_err != 0 && errno != EEXIST)
     217         || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) != 0
     218         || chmod(pw.pw_dir, 02755) != 0 /* set setgid bit on homedir */
     219        ) {
     220            bb_simple_perror_msg(pw.pw_dir);
     221        }
     222    }
     223
     224    if (!(opts & OPT_DONT_SET_PASS)) {
     225        /* interactively set passwd */
     226        passwd_wrapper(pw.pw_name);
     227    }
     228
     229    return EXIT_SUCCESS;
     230}
  • branches/2.2.9/mindi-busybox/loginutils/chpasswd.c

    r1765 r2725  
    44 *
    55 * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org>
    6  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    77 */
    8 
    98#include "libbb.h"
    109
    11 #if ENABLE_GETOPT_LONG
    12 #include <getopt.h>
    13 
     10#if ENABLE_LONG_OPTS
    1411static const char chpasswd_longopts[] ALIGN1 =
    1512    "encrypted\0" No_argument "e"
     
    1815#endif
    1916
    20 #define OPT_ENC     1
    21 #define OPT_MD5     2
     17#define OPT_ENC  1
     18#define OPT_MD5  2
    2219
    23 int chpasswd_main(int argc, char **argv);
    24 int chpasswd_main(int argc, char **argv)
     20int chpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     21int chpasswd_main(int argc UNUSED_PARAM, char **argv)
    2522{
    2623    char *name, *pass;
     
    3330
    3431    opt_complementary = "m--e:e--m";
    35     USE_GETOPT_LONG(applet_long_options = chpasswd_longopts;)
     32    IF_LONG_OPTS(applet_long_options = chpasswd_longopts;)
    3633    opt = getopt32(argv, "em");
    3734
    38     while ((name = xmalloc_getline(stdin)) != NULL) {
     35    while ((name = xmalloc_fgetline(stdin)) != NULL) {
    3936        pass = strchr(name, ':');
    4037        if (!pass)
     
    5047                rnd = crypt_make_salt(salt + 3, 4, rnd);
    5148            }
    52             pass = pw_encrypt(pass, salt);
     49            pass = pw_encrypt(pass, salt, 0);
    5350        }
    5451
     
    5653         * we try to find & change his passwd in /etc/passwd */
    5754#if ENABLE_FEATURE_SHADOWPASSWDS
    58         rc = update_passwd(bb_path_shadow_file, name, pass);
     55        rc = update_passwd(bb_path_shadow_file, name, pass, NULL);
    5956        if (rc == 0) /* no lines updated, no errors detected */
    6057#endif
    61             rc = update_passwd(bb_path_passwd_file, name, pass);
     58            rc = update_passwd(bb_path_passwd_file, name, pass, NULL);
    6259        /* LOGMODE_BOTH logs to syslog also */
    6360        logmode = LOGMODE_BOTH;
     
    6865        logmode = LOGMODE_STDIO;
    6966        free(name);
     67        if (!(opt & OPT_ENC))
     68            free(pass);
    7069    }
    71 
    72     return 0;
     70    return EXIT_SUCCESS;
    7371}
  • branches/2.2.9/mindi-busybox/loginutils/cryptpw.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * cryptpw.c
     3 * cryptpw.c - output a crypt(3)ed password to stdout.
     4 *
     5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    46 *
    57 * Cooked from passwd.c by Thomas Lundquist <thomasez@zelow.no>
     8 * mkpasswd compatible options added by Bernhard Reutner-Fischer
     9 *
     10 * Licensed under GPLv2, see file LICENSE in this source tree.
    611 */
    712
    813#include "libbb.h"
    914
    10 int cryptpw_main(int argc, char **argv);
    11 int cryptpw_main(int argc, char **argv)
     15/* Debian has 'mkpasswd' utility, manpage says:
     16
     17NAME
     18    mkpasswd - Overfeatured front end to crypt(3)
     19SYNOPSIS
     20    mkpasswd PASSWORD SALT
     21...
     22OPTIONS
     23-S, --salt=STRING
     24    Use the STRING as salt. It must not  contain  prefixes  such  as
     25    $1$.
     26-R, --rounds=NUMBER
     27    Use NUMBER rounds. This argument is ignored if the method
     28    choosen does not support variable rounds. For the OpenBSD Blowfish
     29    method this is the logarithm of the number of rounds.
     30-m, --method=TYPE
     31    Compute the password using the TYPE method. If TYPE is 'help'
     32    then the available methods are printed.
     33-P, --password-fd=NUM
     34    Read the password from file descriptor NUM instead of using getpass(3).
     35    If the file descriptor is not connected to a tty then
     36    no other message than the hashed password is printed on stdout.
     37-s, --stdin
     38    Like --password-fd=0.
     39ENVIRONMENT
     40    $MKPASSWD_OPTIONS
     41    A list of options which will be evaluated before the ones
     42    specified on the command line.
     43BUGS
     44    This programs suffers of a bad case of featuritis.
     45    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     46
     47Very true...
     48
     49cryptpw was in bbox before this gem, so we retain it, and alias mkpasswd
     50to cryptpw. -a option (alias for -m) came from cryptpw.
     51*/
     52
     53int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     54int cryptpw_main(int argc UNUSED_PARAM, char **argv)
    1255{
    13     char salt[sizeof("$N$XXXXXXXX")];
     56    /* $N$ + sha_salt_16_bytes + NUL */
     57    char salt[3 + 16 + 1];
     58    char *salt_ptr;
     59    const char *opt_m, *opt_S;
     60    int len;
     61    int fd;
    1462
    15     if (!getopt32(argv, "a:", NULL) || argv[optind - 1][0] != 'd') {
    16         strcpy(salt, "$1$");
    17         /* Too ugly, and needs even more magic to handle endianness: */
    18         //((uint32_t*)&salt)[0] = '$' + '1'*0x100 + '$'*0x10000;
    19         /* Hope one day gcc will do it itself (inlining strcpy) */
    20         crypt_make_salt(salt + 3, 4, 0); /* md5 */
    21     } else {
    22         crypt_make_salt(salt, 1, 0);     /* des */
     63#if ENABLE_LONG_OPTS
     64    static const char mkpasswd_longopts[] ALIGN1 =
     65        "stdin\0"       No_argument       "s"
     66        "password-fd\0" Required_argument "P"
     67        "salt\0"        Required_argument "S"
     68        "method\0"      Required_argument "m"
     69    ;
     70    applet_long_options = mkpasswd_longopts;
     71#endif
     72    fd = STDIN_FILENO;
     73    opt_m = "d";
     74    opt_S = NULL;
     75    /* at most two non-option arguments; -P NUM */
     76    opt_complementary = "?2:P+";
     77    getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m);
     78    argv += optind;
     79
     80    /* have no idea how to handle -s... */
     81
     82    if (argv[0] && !opt_S)
     83        opt_S = argv[1];
     84
     85    len = 2/2;
     86    salt_ptr = salt;
     87    if (opt_m[0] != 'd') { /* not des */
     88        len = 8/2; /* so far assuming md5 */
     89        *salt_ptr++ = '$';
     90        *salt_ptr++ = '1';
     91        *salt_ptr++ = '$';
     92#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
     93        if (opt_m[0] == 's') { /* sha */
     94            salt[1] = '5' + (strcmp(opt_m, "sha512") == 0);
     95            len = 16/2;
     96        }
     97#endif
    2398    }
     99    if (opt_S)
     100        safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3);
     101    else
     102        crypt_make_salt(salt_ptr, len, 0);
    24103
    25     puts(pw_encrypt(argv[optind] ? argv[optind] : xmalloc_getline(stdin), salt));
     104    xmove_fd(fd, STDIN_FILENO);
    26105
    27     return 0;
     106    puts(pw_encrypt(
     107        argv[0] ? argv[0] : (
     108            /* Only mkpasswd, and only from tty, prompts.
     109             * Otherwise it is a plain read. */
     110            (isatty(STDIN_FILENO) && applet_name[0] == 'm')
     111            ? bb_ask_stdin("Password: ")
     112            : xmalloc_fgetline(stdin)
     113        ),
     114        salt, 1));
     115
     116    return EXIT_SUCCESS;
    28117}
  • branches/2.2.9/mindi-busybox/loginutils/deluser.c

    r1765 r2725  
    77 * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
    88 *
    9  * Licensed under GPL version 2, see file LICENSE in this tarball for details.
     9 * Licensed under GPLv2, see file LICENSE in this source tree.
    1010 *
    1111 */
    12 
    1312#include "libbb.h"
    1413
    15 /* Status */
    16 #define STATUS_OK            0
    17 #define NAME_NOT_FOUND       1
    18 #define MEMBER_NOT_FOUND     2
     14int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     15int deluser_main(int argc, char **argv)
     16{
     17    /* User or group name */
     18    char *name;
     19    /* Username (non-NULL only in "delgroup USER GROUP" case) */
     20    char *member;
     21    /* Name of passwd or group file */
     22    const char *pfile;
     23    /* Name of shadow or gshadow file */
     24    const char *sfile;
     25    /* Are we deluser or delgroup? */
     26    int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u'));
    1927
    20 static void del_line_matching(char **args,
    21         const char *filename,
    22         FILE *(*fopen_func)(const char *fileName, const char *mode))
    23 {
    24     FILE *passwd;
    25     smallint error = NAME_NOT_FOUND;
    26     char *name = (ENABLE_FEATURE_DEL_USER_FROM_GROUP && args[2]) ? args[2] : args[1];
    27     char *line, *del;
    28     char *new = xzalloc(1);
     28    if (geteuid() != 0)
     29        bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
    2930
    30     passwd = fopen_func(filename, "r");
    31     if (passwd) {
    32         while ((line = xmalloc_fgets(passwd))) {
    33             int len = strlen(name);
     31    name = argv[1];
     32    member = NULL;
    3433
    35             if (strncmp(line, name, len) == 0
    36              && line[len] == ':'
    37             ) {
    38                 error = STATUS_OK;
    39                 if (ENABLE_FEATURE_DEL_USER_FROM_GROUP) {
    40                     struct group *gr;
    41                     char *p;
    42                     if (args[2]
    43                      /* There were two args on commandline */
    44                      && (gr = getgrnam(name))
    45                      /* The group was not deleted in the meanwhile */
    46                      && (p = strrchr(line, ':'))
    47                      /* We can find a pointer to the last ':' */
    48                     ) {
    49                         error = MEMBER_NOT_FOUND;
    50                         /* Move past ':' (worst case to '\0') and cut the line */
    51                         p[1] = '\0';
    52                         /* Reuse p */
    53                         for (p = xzalloc(1); *gr->gr_mem != NULL; gr->gr_mem++) {
    54                             /* Add all the other group members */
    55                             if (strcmp(args[1], *gr->gr_mem) != 0) {
    56                                 del = p;
    57                                 p = xasprintf("%s%s%s", p, p[0] ? "," : "", *gr->gr_mem);
    58                                 free(del);
    59                             } else
    60                                 error = STATUS_OK;
    61                         }
    62                         /* Recompose the line */
    63                         line = xasprintf("%s%s\n", line, p);
    64                         if (ENABLE_FEATURE_CLEAN_UP) free(p);
    65                     } else
    66                         goto skip;
     34    switch (argc) {
     35    case 3:
     36        if (!ENABLE_FEATURE_DEL_USER_FROM_GROUP || do_deluser)
     37            break;
     38        /* It's "delgroup USER GROUP" */
     39        member = name;
     40        name = argv[2];
     41        /* Fallthrough */
     42
     43    case 2:
     44        if (do_deluser) {
     45            /* "deluser USER" */
     46            xgetpwnam(name); /* bail out if USER is wrong */
     47            pfile = bb_path_passwd_file;
     48            if (ENABLE_FEATURE_SHADOWPASSWDS)
     49                sfile = bb_path_shadow_file;
     50        } else {
     51            struct group *gr;
     52 do_delgroup:
     53            /* "delgroup GROUP" or "delgroup USER GROUP" */
     54            if (do_deluser < 0) { /* delgroup after deluser? */
     55                gr = getgrnam(name);
     56                if (!gr)
     57                    return EXIT_SUCCESS;
     58            } else {
     59                gr = xgetgrnam(name); /* bail out if GROUP is wrong */
     60            }
     61            if (!member) {
     62                /* "delgroup GROUP" */
     63                struct passwd *pw;
     64                struct passwd pwent;
     65                /* Check if the group is in use */
     66#define passwd_buf bb_common_bufsiz1
     67                while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) {
     68                    if (pwent.pw_gid == gr->gr_gid)
     69                        bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name);
    6770                }
     71                //endpwent();
    6872            }
    69             del = new;
    70             new = xasprintf("%s%s", new, line);
    71             free(del);
    72  skip:
    73             free(line);
     73            pfile = bb_path_group_file;
     74            if (ENABLE_FEATURE_SHADOWPASSWDS)
     75                sfile = bb_path_gshadow_file;
    7476        }
    7577
    76         if (ENABLE_FEATURE_CLEAN_UP) fclose(passwd);
     78        /* Modify pfile, then sfile */
     79        do {
     80            if (update_passwd(pfile, name, NULL, member) == -1)
     81                return EXIT_FAILURE;
     82            if (ENABLE_FEATURE_SHADOWPASSWDS) {
     83                pfile = sfile;
     84                sfile = NULL;
     85            }
     86        } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile);
    7787
    78         if (error) {
    79             if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && error == MEMBER_NOT_FOUND) {
    80                 /* Set the correct values for error message */
    81                 filename = name;
    82                 name = args[1];
    83             }
    84             bb_error_msg("can't find %s in %s", name, filename);
    85         } else {
    86             passwd = fopen_func(filename, "w");
    87             if (passwd) {
    88                 fputs(new, passwd);
    89                 if (ENABLE_FEATURE_CLEAN_UP) fclose(passwd);
    90             }
     88        if (ENABLE_DELGROUP && do_deluser > 0) {
     89            /* "deluser USER" also should try to delete
     90             * same-named group. IOW: do "delgroup USER"
     91             */
     92// On debian deluser is a perl script that calls userdel.
     93// From man userdel:
     94//  If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will
     95//  delete the group with the same name as the user.
     96            do_deluser = -1;
     97            goto do_delgroup;
    9198        }
     99        return EXIT_SUCCESS;
    92100    }
    93     free(new);
     101    /* Reached only if number of command line args is wrong */
     102    bb_show_usage();
    94103}
    95 
    96 int deluser_main(int argc, char **argv);
    97 int deluser_main(int argc, char **argv)
    98 {
    99     if (argc == 2
    100      || (ENABLE_FEATURE_DEL_USER_FROM_GROUP
    101         && (applet_name[3] == 'g' && argc == 3))
    102     ) {
    103         if (geteuid())
    104             bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
    105 
    106         if ((ENABLE_FEATURE_DEL_USER_FROM_GROUP && argc != 3)
    107          || ENABLE_DELUSER
    108          || (ENABLE_DELGROUP && ENABLE_DESKTOP)
    109         ) {
    110             if (ENABLE_DELUSER
    111              && (!ENABLE_DELGROUP || applet_name[3] == 'u')
    112             ) {
    113                 del_line_matching(argv, bb_path_passwd_file, xfopen);
    114                 if (ENABLE_FEATURE_SHADOWPASSWDS)
    115                     del_line_matching(argv, bb_path_shadow_file, fopen_or_warn);
    116             } else if (ENABLE_DESKTOP && ENABLE_DELGROUP && getpwnam(argv[1]))
    117                 bb_error_msg_and_die("can't remove primary group of user %s", argv[1]);
    118         }
    119         del_line_matching(argv, bb_path_group_file, xfopen);
    120         if (ENABLE_FEATURE_SHADOWPASSWDS)
    121             del_line_matching(argv, bb_path_gshadow_file, fopen_or_warn);
    122         return EXIT_SUCCESS;
    123     } else
    124         bb_show_usage();
    125 }
  • branches/2.2.9/mindi-busybox/loginutils/getty.c

    r1765 r2725  
    77 * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
    88 *
    9  * 1999-02-22 Arkadiusz Mikiewicz <misiek@misiek.eu.org>
     9 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
    1010 * - added Native Language Support
    11 
     11 *
    1212 * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
    1313 * - enable hardware flow control before displaying /etc/issue
    1414 *
    15  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
    16  *
     15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1716 */
    1817
     
    2120
    2221#if ENABLE_FEATURE_UTMP
    23 #include <utmp.h>
     22# include <utmp.h> /* LOGIN_PROCESS */
     23#endif
     24
     25#ifndef IUCLC
     26# define IUCLC 0
    2427#endif
    2528
     
    2932 */
    3033#ifdef LOGIN_PROCESS                    /* defined in System V utmp.h */
    31 #define SYSV_STYLE                      /* select System V style getty */
    3234#include <sys/utsname.h>
    33 #include <time.h>
    34 #if ENABLE_FEATURE_WTMP
    35 extern void updwtmp(const char *filename, const struct utmp *ut);
    36 static void update_utmp(const char *line);
    37 #endif
     35#else /* if !sysV style, wtmp/utmp code is off */
     36#undef ENABLE_FEATURE_UTMP
     37#undef ENABLE_FEATURE_WTMP
     38#define ENABLE_FEATURE_UTMP 0
     39#define ENABLE_FEATURE_WTMP 0
    3840#endif  /* LOGIN_PROCESS */
    3941
     
    4850/* I doubt there are systems which still need this */
    4951#undef HANDLE_ALLCAPS
     52#undef ANCIENT_BS_KILL_CHARS
    5053
    5154#define _PATH_LOGIN "/bin/login"
     
    5861
    5962/* Some shorthands for control characters. */
    60 #define CTL(x)          (x ^ 0100)      /* Assumes ASCII dialect */
     63#define CTL(x)          ((x) ^ 0100)    /* Assumes ASCII dialect */
    6164#define CR              CTL('M')        /* carriage return */
    6265#define NL              CTL('J')        /* line feed */
     
    7780 * we will try is the first one specified.
    7881 */
    79 #define FIRST_SPEED     0
     82#define MAX_SPEED       10              /* max. nr. of baud rates */
    8083
    8184/* Storage for command-line options. */
    82 
    83 #define MAX_SPEED       10              /* max. nr. of baud rates */
    84 
    8585struct options {
    8686    int flags;                      /* toggle switches, see below */
    8787    unsigned timeout;               /* time-out period */
    88     const char *login;                    /* login program */
    89     const char *tty;                      /* name of tty */
    90     const char *initstring;               /* modem init string */
    91     const char *issue;                    /* alternative issue file */
     88    const char *login;              /* login program */
     89    const char *tty;                /* name of tty */
     90    const char *initstring;         /* modem init string */
     91    const char *issue;              /* alternative issue file */
    9292    int numspeed;                   /* number of baud rates to try */
    9393    int speeds[MAX_SPEED];          /* baud rates to be tried */
    9494};
    95 
    96 static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
    97 #define F_INITSTRING    (1<<0)          /* initstring is set */
    98 #define F_LOCAL         (1<<1)          /* force local */
    99 #define F_FAKEHOST      (1<<2)          /* force fakehost */
    100 #define F_CUSTISSUE     (1<<3)          /* give alternative issue file */
    101 #define F_RTSCTS        (1<<4)          /* enable RTS/CTS flow control */
    102 #define F_ISSUE         (1<<5)          /* display /etc/issue */
    103 #define F_LOGIN         (1<<6)          /* non-default login program */
    104 #define F_PARSE         (1<<7)          /* process modem status messages */
    105 #define F_TIMEOUT       (1<<8)          /* time out */
    106 #define F_WAITCRLF      (1<<9)          /* wait for CR or LF */
    107 #define F_NOPROMPT      (1<<10)         /* don't ask for login name! */
    10895
    10996/* Storage for things detected while the login name was read. */
     
    113100    unsigned char eol;      /* end-of-line character */
    114101    unsigned char parity;   /* what parity did we see */
     102    /* (parity & 1): saw odd parity char with 7th bit set */
     103    /* (parity & 2): saw even parity char with 7th bit set */
     104    /* parity == 0: probably 7-bit, space parity? */
     105    /* parity == 1: probably 7-bit, odd parity? */
     106    /* parity == 2: probably 7-bit, even parity? */
     107    /* parity == 3: definitely 8 bit, no parity! */
     108    /* Hmm... with any value of "parity" 8 bit, no parity is possible */
    115109#ifdef HANDLE_ALLCAPS
    116110    unsigned char capslock; /* upper case without lower case */
    117111#endif
    118112};
     113
    119114
    120115/* Initial values for the above. */
     
    129124};
    130125
     126static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
     127#define F_INITSTRING    (1 << 0)        /* -I initstring is set */
     128#define F_LOCAL         (1 << 1)        /* -L force local */
     129#define F_FAKEHOST      (1 << 2)        /* -H fake hostname */
     130#define F_CUSTISSUE     (1 << 3)        /* -f give alternative issue file */
     131#define F_RTSCTS        (1 << 4)        /* -h enable RTS/CTS flow control */
     132#define F_ISSUE         (1 << 5)        /* -i display /etc/issue */
     133#define F_LOGIN         (1 << 6)        /* -l non-default login program */
     134#define F_PARSE         (1 << 7)        /* -m process modem status messages */
     135#define F_TIMEOUT       (1 << 8)        /* -t time out */
     136#define F_WAITCRLF      (1 << 9)        /* -w wait for CR or LF */
     137#define F_NOPROMPT      (1 << 10)       /* -n don't ask for login name */
     138
     139
     140#define line_buf bb_common_bufsiz1
     141
    131142/* The following is used for understandable diagnostics. */
    132 
    133 /* Fake hostname for ut_host specified on command line. */
    134 static char *fakehost = NULL;
    135 
    136 /* ... */
    137143#ifdef DEBUGGING
    138 #define debug(s) fprintf(dbf,s); fflush(dbf)
     144static FILE *dbf;
    139145#define DEBUGTERM "/dev/ttyp0"
    140 static FILE *dbf;
     146#define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
    141147#else
    142 #define debug(s) /* nothing */
    143 #endif
    144 
    145 
    146 /* bcode - convert speed string to speed code; return 0 on failure */
     148#define debug(...) ((void)0)
     149#endif
     150
     151
     152/* bcode - convert speed string to speed code; return <= 0 on failure */
    147153static int bcode(const char *s)
    148154{
    149     int r;
    150     unsigned value = bb_strtou(s, NULL, 10);
    151     if (errno) {
    152         return -1;
    153     }
    154     r = tty_value_to_baud(value);
    155     if (r > 0) {
    156         return r;
    157     }
    158     return 0;
    159 }
    160 
     155    int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */
     156    if (value < 0) /* bad terminating char, overflow, etc */
     157        return value;
     158    return tty_value_to_baud(value);
     159}
    161160
    162161/* parse_speeds - parse alternate baud rates */
     
    165164    char *cp;
    166165
     166    /* NB: at least one iteration is always done */
    167167    debug("entered parse_speeds\n");
    168     for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
    169         if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
     168    while ((cp = strsep(&arg, ",")) != NULL) {
     169        op->speeds[op->numspeed] = bcode(cp);
     170        if (op->speeds[op->numspeed] < 0)
    170171            bb_error_msg_and_die("bad speed: %s", cp);
     172        /* note: arg "0" turns into speed B0 */
     173        op->numspeed++;
    171174        if (op->numspeed > MAX_SPEED)
    172175            bb_error_msg_and_die("too many alternate speeds");
    173176    }
    174     debug("exiting parsespeeds\n");
    175 }
    176 
     177    debug("exiting parse_speeds\n");
     178}
    177179
    178180/* parse_args - parse command-line arguments */
    179 static void parse_args(int argc, char **argv, struct options *op)
     181static void parse_args(char **argv, struct options *op, char **fakehost_p)
    180182{
    181183    char *ts;
    182184
     185    opt_complementary = "-2:t+"; /* at least 2 args; -t N */
    183186    op->flags = getopt32(argv, opt_string,
    184         &(op->initstring), &fakehost, &(op->issue),
    185         &(op->login), &ts);
     187        &(op->initstring), fakehost_p, &(op->issue),
     188        &(op->login), &op->timeout);
     189    argv += optind;
    186190    if (op->flags & F_INITSTRING) {
    187         const char *p = op->initstring;
    188         char *q;
    189 
    190         op->initstring = q = xstrdup(op->initstring);
    191         /* copy optarg into op->initstring decoding \ddd
    192            octal codes into chars */
    193         while (*p) {
    194             if (*p == '\\') {
    195                 p++;
    196                 *q++ = bb_process_escape_sequence(&p);
    197             } else {
    198                 *q++ = *p++;
    199             }
    200         }
    201         *q = '\0';
    202     }
    203     op->flags ^= F_ISSUE;           /* revert flag show /etc/issue */
    204     if (op->flags & F_TIMEOUT) {
    205         op->timeout = xatoul_range(ts, 1, INT_MAX);
    206     }
    207     argv += optind;
    208     argc -= optind;
    209     debug("after getopt loop\n");
    210     if (argc < 2)          /* check parameter count */
    211         bb_show_usage();
     191        op->initstring = xstrdup(op->initstring);
     192        /* decode \ddd octal codes into chars */
     193        strcpy_and_process_escape_sequences((char*)op->initstring, op->initstring);
     194    }
     195    op->flags ^= F_ISSUE;           /* invert flag "show /etc/issue" */
     196    debug("after getopt\n");
    212197
    213198    /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
     199    op->tty = argv[0];      /* tty name */
     200    ts = argv[1];           /* baud rate(s) */
    214201    if (isdigit(argv[0][0])) {
    215202        /* a number first, assume it's a speed (BSD style) */
    216         parse_speeds(op, argv[0]);       /* baud rate(s) */
    217         op->tty = argv[1]; /* tty name */
    218     } else {
    219         op->tty = argv[0];       /* tty name */
    220         parse_speeds(op, argv[1]); /* baud rate(s) */
    221     }
     203        op->tty = ts;   /* tty name is in argv[1] */
     204        ts = argv[0];   /* baud rate(s) */
     205    }
     206    parse_speeds(op, ts);
     207    applet_name = xasprintf("getty: %s", op->tty);
    222208
    223209    if (argv[2])
    224         setenv("TERM", argv[2], 1);
    225 
    226     debug("exiting parseargs\n");
     210        xsetenv("TERM", argv[2]);
     211
     212    debug("exiting parse_args\n");
    227213}
    228214
    229215/* open_tty - set up tty as standard { input, output, error } */
    230 static void open_tty(const char *tty, struct termios *tp, int local)
    231 {
    232     int chdir_to_root = 0;
    233 
     216static void open_tty(const char *tty)
     217{
    234218    /* Set up new standard input, unless we are given an already opened port. */
    235219    if (NOT_LONE_DASH(tty)) {
    236         struct stat st;
    237         int fd;
     220//      struct stat st;
     221//      int cur_dir_fd;
     222//      int fd;
    238223
    239224        /* Sanity checks... */
    240         xchdir("/dev");
    241         chdir_to_root = 1;
    242         xstat(tty, &st);
    243         if ((st.st_mode & S_IFMT) != S_IFCHR)
    244             bb_error_msg_and_die("%s: not a character device", tty);
     225//      cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
     226//      xchdir("/dev");
     227//      xstat(tty, &st);
     228//      if (!S_ISCHR(st.st_mode))
     229//          bb_error_msg_and_die("not a character device");
     230
     231        if (tty[0] != '/')
     232            tty = xasprintf("/dev/%s", tty); /* will leak it */
    245233
    246234        /* Open the tty as standard input. */
    247235        debug("open(2)\n");
    248         fd = xopen(tty, O_RDWR | O_NONBLOCK);
    249         xdup2(fd, 0);
    250         while (fd > 2)
    251             close(fd--);
     236        close(0);
     237        /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
     238
     239//      /* Restore current directory */
     240//      fchdir(cur_dir_fd);
     241
     242        /* Open the tty as standard input, continued */
     243//      xmove_fd(fd, 0);
     244//      /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
     245//      while (fd > 2)
     246//          close(fd--);
     247
     248        /* Set proper protections and ownership. */
     249        fchown(0, 0, 0);        /* 0:0 */
     250        fchmod(0, 0620);        /* crw--w---- */
    252251    } else {
    253252        /*
     
    258257            bb_error_msg_and_die("stdin is not open for read/write");
    259258    }
    260 
    261     /* Replace current standard output/error fd's with new ones */
    262     debug("duping\n");
    263     xdup2(0, 1);
    264     xdup2(0, 2);
    265 
    266     /*
    267      * The following ioctl will fail if stdin is not a tty, but also when
    268      * there is noise on the modem control lines. In the latter case, the
    269      * common course of action is (1) fix your cables (2) give the modem more
    270      * time to properly reset after hanging up. SunOS users can achieve (2)
    271      * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
    272      * 5 seconds seems to be a good value.
    273      */
    274     ioctl_or_perror_and_die(0, TCGETS, tp, "%s: TCGETS", tty);
    275 
    276     /*
    277      * It seems to be a terminal. Set proper protections and ownership. Mode
    278      * 0622 is suitable for SYSV <4 because /bin/login does not change
    279      * protections. SunOS 4 login will change the protections to 0620 (write
    280      * access for group tty) after the login has succeeded.
    281      */
    282 
    283 #ifdef DEBIAN
    284 #warning Debian /dev/vcs[a]NN hack is deprecated and will be removed
    285     {
    286         /* tty to root.dialout 660 */
    287         struct group *gr;
    288         int id;
    289 
    290         gr = getgrnam("dialout");
    291         id = gr ? gr->gr_gid : 0;
    292         chown(tty, 0, id);
    293         chmod(tty, 0660);
    294 
    295         /* vcs,vcsa to root.sys 600 */
    296         if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
    297             char *vcs, *vcsa;
    298 
    299             vcs = xstrdup(tty);
    300             vcsa = xmalloc(strlen(tty) + 2);
    301             strcpy(vcs, "vcs");
    302             strcpy(vcs + 3, tty + 3);
    303             strcpy(vcsa, "vcsa");
    304             strcpy(vcsa + 4, tty + 3);
    305 
    306             gr = getgrnam("sys");
    307             id = gr ? gr->gr_gid : 0;
    308             chown(vcs, 0, id);
    309             chmod(vcs, 0600);
    310             chown(vcsa, 0, id);
    311             chmod(vcs, 0600);
    312 
    313             free(vcs);
    314             free(vcsa);
    315         }
    316     }
    317 #else
    318     if (NOT_LONE_DASH(tty)) {
    319         chown(tty, 0, 0);        /* 0:0 */
    320         chmod(tty, 0622);        /* crw--w--w- */
    321     }
    322 #endif
    323     if (chdir_to_root)
    324         xchdir("/");
    325259}
    326260
     
    328262static void termios_init(struct termios *tp, int speed, struct options *op)
    329263{
     264    speed_t ispeed, ospeed;
    330265    /*
    331266     * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
     
    334269     * later on.
    335270     */
    336 #ifdef __linux__
    337271    /* flush input and output queues, important for modems! */
    338     ioctl(0, TCFLSH, TCIOFLUSH);
    339 #endif
    340 
    341     tp->c_cflag = CS8 | HUPCL | CREAD | speed;
    342     if (op->flags & F_LOCAL) {
     272    tcflush(0, TCIOFLUSH);
     273    ispeed = ospeed = speed;
     274    if (speed == B0) {
     275        /* Speed was specified as "0" on command line.
     276         * Just leave it unchanged */
     277        ispeed = cfgetispeed(tp);
     278        ospeed = cfgetospeed(tp);
     279    }
     280    tp->c_cflag = CS8 | HUPCL | CREAD;
     281    if (op->flags & F_LOCAL)
    343282        tp->c_cflag |= CLOCAL;
    344     }
    345 
    346     tp->c_iflag = tp->c_lflag = tp->c_line = 0;
     283    cfsetispeed(tp, ispeed);
     284    cfsetospeed(tp, ospeed);
     285
     286    tp->c_iflag = tp->c_lflag = 0;
    347287    tp->c_oflag = OPOST | ONLCR;
    348288    tp->c_cc[VMIN] = 1;
    349289    tp->c_cc[VTIME] = 0;
     290#ifdef __linux__
     291    tp->c_line = 0;
     292#endif
    350293
    351294    /* Optionally enable hardware flow control */
    352 
    353 #ifdef  CRTSCTS
     295#ifdef CRTSCTS
    354296    if (op->flags & F_RTSCTS)
    355297        tp->c_cflag |= CRTSCTS;
    356298#endif
    357299
    358     ioctl(0, TCSETS, tp);
    359 
    360     /* go to blocking input even in local mode */
    361     ndelay_off(0);
     300    tcsetattr_stdin_TCSANOW(tp);
    362301
    363302    debug("term_io 2\n");
     
    392331     * be dealt with later on.
    393332     */
    394 
    395333    iflag = tp->c_iflag;
    396334    tp->c_iflag |= ISTRIP;          /* enable 8th-bit stripping */
    397335    vmin = tp->c_cc[VMIN];
    398     tp->c_cc[VMIN] = 0;                     /* don't block if queue empty */
    399     ioctl(0, TCSETS, tp);
     336    tp->c_cc[VMIN] = 0;             /* don't block if queue empty */
     337    tcsetattr_stdin_TCSANOW(tp);
    400338
    401339    /*
     
    403341     * try to extract the speed of the dial-in call.
    404342     */
    405 
    406343    sleep(1);
    407     nread = read(0, buf, size_buf - 1);
     344    nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
    408345    if (nread > 0) {
    409346        buf[nread] = '\0';
    410347        for (bp = buf; bp < buf + nread; bp++) {
    411             if (isascii(*bp) && isdigit(*bp)) {
     348            if (isdigit(*bp)) {
    412349                speed = bcode(bp);
    413                 if (speed) {
    414                     tp->c_cflag &= ~CBAUD;
    415                     tp->c_cflag |= speed;
    416                 }
     350                if (speed > 0)
     351                    cfsetspeed(tp, speed);
    417352                break;
    418353            }
    419354        }
    420355    }
     356
    421357    /* Restore terminal settings. Errors will be dealt with later on. */
    422 
    423358    tp->c_iflag = iflag;
    424359    tp->c_cc[VMIN] = vmin;
    425     ioctl(0, TCSETS, tp);
    426 }
    427 
    428 /* next_speed - select next baud rate */
    429 static void next_speed(struct termios *tp, struct options *op)
    430 {
    431     static int baud_index = FIRST_SPEED;    /* current speed index */
    432 
    433     baud_index = (baud_index + 1) % op->numspeed;
    434     tp->c_cflag &= ~CBAUD;
    435     tp->c_cflag |= op->speeds[baud_index];
    436     ioctl(0, TCSETS, tp);
    437 }
    438 
     360    tcsetattr_stdin_TCSANOW(tp);
     361}
    439362
    440363/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
    441 static void do_prompt(struct options *op, struct termios *tp)
     364static void do_prompt(struct options *op)
    442365{
    443366#ifdef ISSUE
     
    448371
    449372#ifdef HANDLE_ALLCAPS
    450 /* caps_lock - string contains upper case without lower case */
     373/* all_is_upcase - string contains upper case without lower case */
    451374/* returns 1 if true, 0 if false */
    452 static int caps_lock(const char *s)
     375static int all_is_upcase(const char *s)
    453376{
    454377    while (*s)
     
    459382#endif
    460383
    461 /* get_logname - get user name, establish parity, speed, erase, kill, eol */
    462 /* return NULL on failure, logname on success */
     384/* get_logname - get user name, establish parity, speed, erase, kill, eol;
     385 * return NULL on BREAK, logname on success */
    463386static char *get_logname(char *logname, unsigned size_logname,
    464         struct options *op, struct chardata *cp, struct termios *tp)
     387        struct options *op, struct chardata *cp)
    465388{
    466389    char *bp;
    467     char c;             /* input character, full eight bits */
     390    char c;                         /* input character, full eight bits */
    468391    char ascval;                    /* low 7 bits of input character */
    469392    int bits;                       /* # of "1" bits per character */
    470393    int mask;                       /* mask with 1 bit up */
    471     static const char erase[][3] = {    /* backspace-space-backspace */
     394    static const char erase[][3] = {/* backspace-space-backspace */
    472395        "\010\040\010",                 /* space parity */
    473396        "\010\040\010",                 /* odd parity */
    474397        "\210\240\210",                 /* even parity */
    475         "\210\240\210",                 /* no parity */
     398        "\010\040\010",                 /* 8 bit no parity */
    476399    };
    477400
    478     /* Initialize kill, erase, parity etc. (also after switching speeds). */
    479 
    480     *cp = init_chardata;
     401    /* NB: *cp is pre-initialized with init_chardata */
    481402
    482403    /* Flush pending input (esp. after parsing or switching the baud rate). */
    483 
    484404    sleep(1);
    485     ioctl(0, TCFLSH, TCIFLUSH);
     405    tcflush(0, TCIOFLUSH);
    486406
    487407    /* Prompt for and read a login name. */
    488 
    489408    logname[0] = '\0';
    490409    while (!logname[0]) {
    491 
    492410        /* Write issue file and prompt, with "parity" bit == 0. */
    493 
    494         do_prompt(op, tp);
     411        do_prompt(op);
    495412
    496413        /* Read name, watch for break, parity, erase, kill, end-of-line. */
    497 
    498414        bp = logname;
    499415        cp->eol = '\0';
     
    501417
    502418            /* Do not report trivial EINTR/EIO errors. */
    503             if (read(0, &c, 1) < 1) {
     419            errno = EINTR; /* make read of 0 bytes be silent too */
     420            if (read(STDIN_FILENO, &c, 1) < 1) {
    504421                if (errno == EINTR || errno == EIO)
    505                     exit(0);
    506                 bb_perror_msg_and_die("%s: read", op->tty);
     422                    exit(EXIT_SUCCESS);
     423                bb_perror_msg_and_die(bb_msg_read_error);
    507424            }
    508425
    509             /* Do BREAK handling elsewhere. */
     426            /* BREAK. If we have speeds to try,
     427             * return NULL (will switch speeds and return here) */
    510428            if (c == '\0' && op->numspeed > 1)
    511429                return NULL;
    512430
    513431            /* Do parity bit handling. */
    514             ascval = c & 0177;
    515             if (c != ascval) {       /* "parity" bit on ? */
     432            if (!(op->flags & F_LOCAL) && (c & 0x80)) {       /* "parity" bit on? */
    516433                bits = 1;
    517434                mask = 1;
    518                 while (mask & 0177) {
    519                     if (mask & ascval)
     435                while (mask & 0x7f) {
     436                    if (mask & c)
    520437                        bits++; /* count "1" bits */
    521438                    mask <<= 1;
     
    526443
    527444            /* Do erase, kill and end-of-line processing. */
     445            ascval = c & 0x7f;
    528446            switch (ascval) {
    529447            case CR:
     
    534452            case BS:
    535453            case DEL:
     454#ifdef ANCIENT_BS_KILL_CHARS
    536455            case '#':
     456#endif
    537457                cp->erase = ascval;     /* set erase character */
    538458                if (bp > logname) {
    539                     write(1, erase[cp->parity], 3);
     459                    full_write(STDOUT_FILENO, erase[cp->parity], 3);
    540460                    bp--;
    541461                }
    542462                break;
    543463            case CTL('U'):
     464#ifdef ANCIENT_BS_KILL_CHARS
    544465            case '@':
     466#endif
    545467                cp->kill = ascval;      /* set kill character */
    546468                while (bp > logname) {
    547                     write(1, erase[cp->parity], 3);
     469                    full_write(STDOUT_FILENO, erase[cp->parity], 3);
    548470                    bp--;
    549471                }
    550472                break;
    551473            case CTL('D'):
    552                 exit(0);
     474                exit(EXIT_SUCCESS);
    553475            default:
    554                 if (!isascii(ascval) || !isprint(ascval)) {
     476                if (ascval < ' ') {
    555477                    /* ignore garbage characters */
    556                 } else if (bp - logname >= size_logname - 1) {
    557                     bb_error_msg_and_die("%s: input overrun", op->tty);
     478                } else if ((int)(bp - logname) >= size_logname - 1) {
     479                    bb_error_msg_and_die("input overrun");
    558480                } else {
    559                     write(1, &c, 1); /* echo the character */
     481                    full_write(STDOUT_FILENO, &c, 1); /* echo the character */
    560482                    *bp++ = ascval; /* and store it */
    561483                }
     
    567489
    568490#ifdef HANDLE_ALLCAPS
    569     cp->capslock = caps_lock(logname);
     491    cp->capslock = all_is_upcase(logname);
    570492    if (cp->capslock) {
    571493        for (bp = logname; *bp; bp++)
     
    581503{
    582504    /* General terminal-independent stuff. */
    583 
    584505    tp->c_iflag |= IXON | IXOFF;    /* 2-way flow control */
    585506    tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
     
    591512    tp->c_cc[VEOF] = DEF_EOF;       /* default EOF character */
    592513    tp->c_cc[VEOL] = DEF_EOL;
     514#ifdef VSWTC
    593515    tp->c_cc[VSWTC] = DEF_SWITCH;   /* default switch character */
     516#endif
    594517
    595518    /* Account for special characters seen in input. */
    596 
    597519    if (cp->eol == CR) {
    598520        tp->c_iflag |= ICRNL;   /* map CR in input to NL */
     
    603525
    604526    /* Account for the presence or absence of parity bits in input. */
    605 
    606527    switch (cp->parity) {
    607528    case 0:                                 /* space (always 0) parity */
     529// I bet most people go here - they use only 7-bit chars in usernames....
    608530        break;
    609531    case 1:                                 /* odd parity */
     
    617539        tp->c_cflag &= ~CSIZE;
    618540        tp->c_cflag |= CS7;
     541// FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
     542// it's probably some umlauts etc, but definitely NOT 7-bit!!!
     543// Entire parity detection madness here just begs for deletion...
    619544        break;
    620545    }
     
    629554#endif
    630555    /* Optionally enable hardware flow control */
    631 
    632 #ifdef  CRTSCTS
     556#ifdef CRTSCTS
    633557    if (op->flags & F_RTSCTS)
    634558        tp->c_cflag |= CRTSCTS;
     
    636560
    637561    /* Finally, make the new settings effective */
    638 
    639     ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
    640 }
    641 
    642 
    643 #ifdef SYSV_STYLE
    644 #if ENABLE_FEATURE_UTMP
    645 /* update_utmp - update our utmp entry */
    646 static void update_utmp(const char *line)
    647 {
    648     struct utmp ut;
    649     struct utmp *utp;
    650     time_t t;
    651     int mypid = getpid();
    652 
    653     /*
    654      * The utmp file holds miscellaneous information about things started by
    655      * /sbin/init and other system-related events. Our purpose is to update
    656      * the utmp entry for the current process, in particular the process type
    657      * and the tty line we are listening to. Return successfully only if the
    658      * utmp file can be opened for update, and if we are able to find our
    659      * entry in the utmp file.
    660      */
    661     if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
    662         close(creat(_PATH_UTMP, 0664));
    663     }
    664     utmpname(_PATH_UTMP);
    665     setutent();
    666     while ((utp = getutent())
    667            && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid))
    668         /* nothing */;
    669 
    670     if (utp) {
    671         memcpy(&ut, utp, sizeof(ut));
    672     } else {
    673         /* some inits don't initialize utmp... */
    674         memset(&ut, 0, sizeof(ut));
    675         safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
    676     }
    677     /* endutent(); */
    678 
    679     strcpy(ut.ut_user, "LOGIN");
    680     safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
    681     if (fakehost)
    682         safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
    683     time(&t);
    684     ut.ut_time = t;
    685     ut.ut_type = LOGIN_PROCESS;
    686     ut.ut_pid = mypid;
    687 
    688     pututline(&ut);
    689     endutent();
    690 
    691 #if ENABLE_FEATURE_WTMP
    692     if (access(bb_path_wtmp_file, R_OK|W_OK) == -1)
    693         close(creat(bb_path_wtmp_file, 0664));
    694     updwtmp(bb_path_wtmp_file, &ut);
    695 #endif
    696 }
    697 
    698 #endif /* CONFIG_FEATURE_UTMP */
    699 #endif /* SYSV_STYLE */
    700 
    701 
    702 int getty_main(int argc, char **argv);
    703 int getty_main(int argc, char **argv)
    704 {
    705     int nullfd;
    706     char *logname = NULL;           /* login name, given to /bin/login */
     562    if (tcsetattr_stdin_TCSANOW(tp) < 0)
     563        bb_perror_msg_and_die("tcsetattr");
     564}
     565
     566int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     567int getty_main(int argc UNUSED_PARAM, char **argv)
     568{
     569    int n;
     570    pid_t pid;
     571    char *fakehost = NULL;          /* Fake hostname for ut_host */
     572    char *logname;                  /* login name, given to /bin/login */
    707573    /* Merging these into "struct local" may _seem_ to reduce
    708574     * parameter passing, but today's gcc will inline
    709575     * statics which are called once anyway, so don't do that */
    710576    struct chardata chardata;       /* set by get_logname() */
    711     struct termios termios;           /* terminal mode bits */
    712     struct options options = {
    713         0,                      /* show /etc/issue (SYSV_STYLE) */
    714         0,                      /* no timeout */
    715         _PATH_LOGIN,            /* default login program */
    716         "tty1",                 /* default tty line */
    717         "",                     /* modem init string */
     577    struct termios termios;         /* terminal mode bits */
     578    struct options options;
     579
     580    chardata = init_chardata;
     581
     582    memset(&options, 0, sizeof(options));
     583    options.login = _PATH_LOGIN;    /* default login program */
     584    options.tty = "tty1";           /* default tty line */
     585    options.initstring = "";        /* modem init string */
    718586#ifdef ISSUE
    719         ISSUE,                  /* default issue file */
    720 #else
    721         NULL,
    722 #endif
    723         0,                      /* no baud rates known yet */
    724     };
    725 
    726     /* Already too late because of theoretical
    727      * possibility of getty --help somehow triggered
    728      * inadvertently before we reach this. Oh well. */
     587    options.issue = ISSUE;          /* default issue file */
     588#endif
     589
     590    /* Parse command-line arguments. */
     591    parse_args(argv, &options, &fakehost);
     592
    729593    logmode = LOGMODE_NONE;
     594
     595    /* Create new session, lose controlling tty, if any */
     596    /* docs/ctty.htm says:
     597     * "This is allowed only when the current process
     598     *  is not a process group leader" - is this a problem? */
    730599    setsid();
    731     nullfd = xopen(bb_dev_null, O_RDWR);
    732     /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */
    733     /* open_tty() will take care of fd# 0 anyway */
    734     dup2(nullfd, 1);
    735     dup2(nullfd, 2);
    736     while (nullfd > 2) close(nullfd--);
    737     /* We want special flavor of error_msg_and_die */
     600    /* close stdio, and stray descriptors, just in case */
     601    n = xopen(bb_dev_null, O_RDWR);
     602    /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
     603    xdup2(n, 1);
     604    xdup2(n, 2);
     605    while (n > 2)
     606        close(n--);
     607
     608    /* Logging. We want special flavor of error_msg_and_die */
    738609    die_sleep = 10;
    739610    msg_eol = "\r\n";
     611    /* most likely will internally use fd #3 in CLOEXEC mode: */
    740612    openlog(applet_name, LOG_PID, LOG_AUTH);
    741613    logmode = LOGMODE_BOTH;
    742614
    743615#ifdef DEBUGGING
    744     dbf = xfopen(DEBUGTERM, "w");
    745 
    746     {
    747         int i;
    748 
    749         for (i = 1; i < argc; i++) {
    750             debug(argv[i]);
    751             debug("\n");
    752         }
    753     }
    754 #endif
    755 
    756     /* Parse command-line arguments. */
    757     parse_args(argc, argv, &options);
    758 
    759 #ifdef SYSV_STYLE
    760 #if ENABLE_FEATURE_UTMP
    761     /* Update the utmp file. */
    762     update_utmp(options.tty);
    763 #endif
    764 #endif
    765 
     616    dbf = xfopen_for_write(DEBUGTERM);
     617    for (n = 1; argv[n]; n++) {
     618        debug(argv[n]);
     619        debug("\n");
     620    }
     621#endif
     622
     623    /* Open the tty as standard input, if it is not "-" */
     624    /* If it's not "-" and not taken yet, it will become our ctty */
    766625    debug("calling open_tty\n");
    767     /* Open the tty as standard { input, output, error }. */
    768     open_tty(options.tty, &termios, options.flags & F_LOCAL);
    769 
     626    open_tty(options.tty);
     627    ndelay_off(0);
     628    debug("duping\n");
     629    xdup2(0, 1);
     630    xdup2(0, 2);
     631
     632    /*
     633     * The following ioctl will fail if stdin is not a tty, but also when
     634     * there is noise on the modem control lines. In the latter case, the
     635     * common course of action is (1) fix your cables (2) give the modem more
     636     * time to properly reset after hanging up. SunOS users can achieve (2)
     637     * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
     638     * 5 seconds seems to be a good value.
     639     */
     640    if (tcgetattr(0, &termios) < 0)
     641        bb_perror_msg_and_die("tcgetattr");
     642
     643    pid = getpid();
    770644#ifdef __linux__
    771     {
    772         int iv;
    773 
    774         iv = getpid();
    775         ioctl(0, TIOCSPGRP, &iv);
    776     }
    777 #endif
     645// FIXME: do we need this? Otherwise "-" case seems to be broken...
     646    // /* Forcibly make fd 0 our controlling tty, even if another session
     647    //  * has it as a ctty. (Another session loses ctty). */
     648    // ioctl(0, TIOCSCTTY, (void*)1);
     649    /* Make ourself a foreground process group within our session */
     650    tcsetpgrp(0, pid);
     651#endif
     652
     653    /* Update the utmp file. This tty is ours now! */
     654    update_utmp(pid, LOGIN_PROCESS, options.tty, "LOGIN", fakehost);
     655
    778656    /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
    779657    debug("calling termios_init\n");
    780     termios_init(&termios, options.speeds[FIRST_SPEED], &options);
    781 
    782     /* write the modem init string and DON'T flush the buffers */
     658    termios_init(&termios, options.speeds[0], &options);
     659
     660    /* Write the modem init string and DON'T flush the buffers */
    783661    if (options.flags & F_INITSTRING) {
    784662        debug("writing init string\n");
    785         write(1, options.initstring, strlen(options.initstring));
    786     }
    787 
    788     if (!(options.flags & F_LOCAL)) {
    789         /* go to blocking write mode unless -L is specified */
    790         ndelay_off(1);
    791     }
    792 
    793     /* Optionally detect the baud rate from the modem status message. */
     663        full_write1_str(options.initstring);
     664    }
     665
     666    /* Optionally detect the baud rate from the modem status message */
    794667    debug("before autobaud\n");
    795668    if (options.flags & F_PARSE)
    796         auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios);
    797 
    798     /* Set the optional timer. */
    799     if (options.timeout)
    800         alarm(options.timeout);
    801 
    802     /* optionally wait for CR or LF before writing /etc/issue */
     669        auto_baud(line_buf, sizeof(line_buf), &termios);
     670
     671    /* Set the optional timer */
     672    alarm(options.timeout); /* if 0, alarm is not set */
     673
     674    /* Optionally wait for CR or LF before writing /etc/issue */
    803675    if (options.flags & F_WAITCRLF) {
    804676        char ch;
    805677
    806678        debug("waiting for cr-lf\n");
    807         while (read(0, &ch, 1) == 1) {
     679        while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
     680            debug("read %x\n", (unsigned char)ch);
    808681            ch &= 0x7f;                     /* strip "parity bit" */
    809 #ifdef DEBUGGING
    810             fprintf(dbf, "read %c\n", ch);
    811 #endif
    812682            if (ch == '\n' || ch == '\r')
    813683                break;
     
    815685    }
    816686
    817     chardata = init_chardata;
     687    logname = NULL;
    818688    if (!(options.flags & F_NOPROMPT)) {
    819         /* Read the login name. */
    820         debug("reading login name\n");
    821         logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1),
    822                 &options, &chardata, &termios);
    823         while (logname == NULL)
    824             next_speed(&termios, &options);
     689        /* NB:termios_init already set line speed
     690         * to options.speeds[0] */
     691        int baud_index = 0;
     692
     693        while (1) {
     694            /* Read the login name. */
     695            debug("reading login name\n");
     696            logname = get_logname(line_buf, sizeof(line_buf),
     697                    &options, &chardata);
     698            if (logname)
     699                break;
     700            /* we are here only if options.numspeed > 1 */
     701            baud_index = (baud_index + 1) % options.numspeed;
     702            cfsetispeed(&termios, options.speeds[baud_index]);
     703            cfsetospeed(&termios, options.speeds[baud_index]);
     704            tcsetattr_stdin_TCSANOW(&termios);
     705        }
    825706    }
    826707
    827708    /* Disable timer. */
    828 
    829     if (options.timeout)
    830         alarm(0);
     709    alarm(0);
    831710
    832711    /* Finalize the termios settings. */
    833 
    834712    termios_final(&options, &termios, &chardata);
    835713
    836714    /* Now the newline character should be properly written. */
    837 
    838     write(1, "\n", 1);
     715    full_write(STDOUT_FILENO, "\n", 1);
    839716
    840717    /* Let the login program take care of password validation. */
    841 
    842     execl(options.login, options.login, "--", logname, (char *) 0);
    843     bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
    844 }
     718    /* We use PATH because we trust that root doesn't set "bad" PATH,
     719     * and getty is not suid-root applet. */
     720    /* With -n, logname == NULL, and login will ask for username instead */
     721    BB_EXECLP(options.login, options.login, "--", logname, NULL);
     722    bb_error_msg_and_die("can't execute '%s'", options.login);
     723}
  • branches/2.2.9/mindi-busybox/loginutils/login.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    44 */
    5 
    65#include "libbb.h"
    7 #include <utmp.h>
     6#include <syslog.h>
     7#if ENABLE_FEATURE_UTMP
     8# include <utmp.h> /* USER_PROCESS */
     9#endif
    810#include <sys/resource.h>
    9 #include <syslog.h>
    1011
    1112#if ENABLE_SELINUX
    12 #include <selinux/selinux.h>  /* for is_selinux_enabled()  */
    13 #include <selinux/get_context_list.h> /* for get_default_context() */
    14 #include <selinux/flask.h> /* for security class definitions  */
     13# include <selinux/selinux.h>  /* for is_selinux_enabled()  */
     14# include <selinux/get_context_list.h> /* for get_default_context() */
     15# include <selinux/flask.h> /* for security class definitions  */
    1516#endif
    1617
    1718#if ENABLE_PAM
    1819/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
    19 #undef setlocale
     20# undef setlocale
    2021/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
    2122 * Apparently they like to confuse people. */
    22 #include <security/pam_appl.h>
    23 #include <security/pam_misc.h>
     23# include <security/pam_appl.h>
     24# include <security/pam_misc.h>
    2425static const struct pam_conv conv = {
    2526    misc_conv,
     
    3738static char* short_tty;
    3839
    39 #if ENABLE_FEATURE_UTMP
    40 /* vv  Taken from tinylogin utmp.c  vv */
    41 /*
    42  * read_or_build_utent - see if utmp file is correct for this process
    43  *
    44  *  System V is very picky about the contents of the utmp file
    45  *  and requires that a slot for the current process exist.
    46  *  The utmp file is scanned for an entry with the same process
    47  *  ID.  If no entry exists the process exits with a message.
    48  *
    49  *  The "picky" flag is for network and other logins that may
    50  *  use special flags.  It allows the pid checks to be overridden.
    51  *  This means that getty should never invoke login with any
    52  *  command line flags.
    53  */
    54 
    55 static void read_or_build_utent(struct utmp *utptr, int picky)
    56 {
    57     struct utmp *ut;
    58     pid_t pid = getpid();
    59 
    60     setutent();
    61 
    62     /* First, try to find a valid utmp entry for this process.  */
    63     while ((ut = getutent()))
    64         if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
    65         (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
    66             break;
    67 
    68     /* If there is one, just use it, otherwise create a new one.  */
    69     if (ut) {
    70         *utptr = *ut;
    71     } else {
    72         if (picky)
    73             bb_error_msg_and_die("no utmp entry found");
    74 
    75         memset(utptr, 0, sizeof(*utptr));
    76         utptr->ut_type = LOGIN_PROCESS;
    77         utptr->ut_pid = pid;
    78         strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line));
    79         /* This one is only 4 chars wide. Try to fit something
    80          * remotely meaningful by skipping "tty"... */
    81         strncpy(utptr->ut_id, short_tty + 3, sizeof(utptr->ut_id));
    82         strncpy(utptr->ut_user, "LOGIN", sizeof(utptr->ut_user));
    83         utptr->ut_time = time(NULL);
    84     }
    85     if (!picky) /* root login */
    86         memset(utptr->ut_host, 0, sizeof(utptr->ut_host));
    87 }
    88 
    89 /*
    90  * write_utent - put a USER_PROCESS entry in the utmp file
    91  *
    92  *  write_utent changes the type of the current utmp entry to
    93  *  USER_PROCESS.  the wtmp file will be updated as well.
    94  */
    95 static void write_utent(struct utmp *utptr, const char *username)
    96 {
    97     utptr->ut_type = USER_PROCESS;
    98     strncpy(utptr->ut_user, username, sizeof(utptr->ut_user));
    99     utptr->ut_time = time(NULL);
    100     /* other fields already filled in by read_or_build_utent above */
    101     setutent();
    102     pututline(utptr);
    103     endutent();
    104 #if ENABLE_FEATURE_WTMP
    105     if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
    106         close(creat(bb_path_wtmp_file, 0664));
    107     }
    108     updwtmp(bb_path_wtmp_file, utptr);
    109 #endif
    110 }
    111 #else /* !ENABLE_FEATURE_UTMP */
    112 #define read_or_build_utent(utptr, picky) ((void)0)
    113 #define write_utent(utptr, username) ((void)0)
    114 #endif /* !ENABLE_FEATURE_UTMP */
    115 
    11640#if ENABLE_FEATURE_NOLOGIN
    117 static void die_if_nologin_and_non_root(int amroot)
     41static void die_if_nologin(void)
    11842{
    11943    FILE *fp;
    12044    int c;
    121 
    122     if (access("/etc/nologin", F_OK))
     45    int empty = 1;
     46
     47    fp = fopen_for_read("/etc/nologin");
     48    if (!fp) /* assuming it does not exist */
    12349        return;
    12450
    125     fp = fopen("/etc/nologin", "r");
    126     if (fp) {
    127         while ((c = getc(fp)) != EOF)
    128             putchar((c=='\n') ? '\r' : c);
    129         fflush(stdout);
    130         fclose(fp);
    131     } else
     51    while ((c = getc(fp)) != EOF) {
     52        if (c == '\n')
     53            bb_putchar('\r');
     54        bb_putchar(c);
     55        empty = 0;
     56    }
     57    if (empty)
    13258        puts("\r\nSystem closed for routine maintenance\r");
    133     if (!amroot)
    134         exit(1);
    135     puts("\r\n[Disconnect bypassed -- root login allowed]\r");
     59
     60    fclose(fp);
     61    fflush_all();
     62    /* Users say that they do need this prior to exit: */
     63    tcdrain(STDOUT_FILENO);
     64    exit(EXIT_FAILURE);
    13665}
    13766#else
    138 static ALWAYS_INLINE void die_if_nologin_and_non_root(int amroot) {}
     67# define die_if_nologin() ((void)0)
    13968#endif
    14069
     
    14271static int check_securetty(void)
    14372{
    144     FILE *fp;
    145     int i;
    146     char buf[256];
    147 
    148     fp = fopen("/etc/securetty", "r");
    149     if (!fp) {
    150         /* A missing securetty file is not an error. */
    151         return 1;
    152     }
    153     while (fgets(buf, sizeof(buf)-1, fp)) {
    154         for (i = strlen(buf)-1; i >= 0; --i) {
    155             if (!isspace(buf[i]))
    156                 break;
    157         }
    158         buf[++i] = '\0';
    159         if (!buf[0] || (buf[0] == '#'))
    160             continue;
    161         if (strcmp(buf, short_tty) == 0) {
    162             fclose(fp);
    163             return 1;
    164         }
    165     }
    166     fclose(fp);
    167     return 0;
     73    char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
     74    parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
     75    while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
     76        if (strcmp(buf, short_tty) == 0)
     77            break;
     78        buf = NULL;
     79    }
     80    config_close(parser);
     81    /* buf != NULL here if config file was not found, empty
     82     * or line was found which equals short_tty */
     83    return buf != NULL;
    16884}
    16985#else
    17086static ALWAYS_INLINE int check_securetty(void) { return 1; }
     87#endif
     88
     89#if ENABLE_SELINUX
     90static void initselinux(char *username, char *full_tty,
     91                        security_context_t *user_sid)
     92{
     93    security_context_t old_tty_sid, new_tty_sid;
     94
     95    if (!is_selinux_enabled())
     96        return;
     97
     98    if (get_default_context(username, NULL, user_sid)) {
     99        bb_error_msg_and_die("can't get SID for %s", username);
     100    }
     101    if (getfilecon(full_tty, &old_tty_sid) < 0) {
     102        bb_perror_msg_and_die("getfilecon(%s) failed", full_tty);
     103    }
     104    if (security_compute_relabel(*user_sid, old_tty_sid,
     105                SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
     106        bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty);
     107    }
     108    if (setfilecon(full_tty, new_tty_sid) != 0) {
     109        bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid);
     110    }
     111}
     112#endif
     113
     114#if ENABLE_LOGIN_SCRIPTS
     115static void run_login_script(struct passwd *pw, char *full_tty)
     116{
     117    char *t_argv[2];
     118
     119    t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
     120    if (t_argv[0]) {
     121        t_argv[1] = NULL;
     122        xsetenv("LOGIN_TTY", full_tty);
     123        xsetenv("LOGIN_USER", pw->pw_name);
     124        xsetenv("LOGIN_UID", utoa(pw->pw_uid));
     125        xsetenv("LOGIN_GID", utoa(pw->pw_gid));
     126        xsetenv("LOGIN_SHELL", pw->pw_shell);
     127        spawn_and_wait(t_argv); /* NOMMU-friendly */
     128        unsetenv("LOGIN_TTY");
     129        unsetenv("LOGIN_USER");
     130        unsetenv("LOGIN_UID");
     131        unsetenv("LOGIN_GID");
     132        unsetenv("LOGIN_SHELL");
     133    }
     134}
     135#else
     136void run_login_script(struct passwd *pw, char *full_tty);
    171137#endif
    172138
     
    181147    do {
    182148        c = getchar();
    183         if (c == EOF) exit(1);
     149        if (c == EOF)
     150            exit(EXIT_FAILURE);
    184151        if (c == '\n') {
    185             if (!--cntdown) exit(1);
     152            if (!--cntdown)
     153                exit(EXIT_FAILURE);
    186154            goto prompt;
    187155        }
    188     } while (isspace(c));
     156    } while (isspace(c)); /* maybe isblank? */
    189157
    190158    *buf++ = c;
    191159    if (!fgets(buf, size_buf-2, stdin))
    192         exit(1);
     160        exit(EXIT_FAILURE);
    193161    if (!strchr(buf, '\n'))
    194         exit(1);
    195     while (isgraph(*buf)) buf++;
     162        exit(EXIT_FAILURE);
     163    while ((unsigned char)*buf > ' ')
     164        buf++;
    196165    *buf = '\0';
    197166}
     
    202171
    203172    fd = open(bb_path_motd_file, O_RDONLY);
    204     if (fd) {
    205         fflush(stdout);
     173    if (fd >= 0) {
     174        fflush_all();
    206175        bb_copyfd_eof(fd, STDOUT_FILENO);
    207176        close(fd);
     
    209178}
    210179
    211 static void alarm_handler(int sig ATTRIBUTE_UNUSED)
     180static void alarm_handler(int sig UNUSED_PARAM)
    212181{
    213182    /* This is the escape hatch!  Poor serial line users and the like
     
    215184     * We don't want to block here */
    216185    ndelay_on(1);
    217     ndelay_on(2);
    218186    printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT);
    219     exit(EXIT_SUCCESS);
    220 }
    221 
    222 int login_main(int argc, char **argv);
    223 int login_main(int argc, char **argv)
     187    fflush_all();
     188    /* unix API is brain damaged regarding O_NONBLOCK,
     189     * we should undo it, or else we can affect other processes */
     190    ndelay_off(1);
     191    _exit(EXIT_SUCCESS);
     192}
     193
     194int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     195int login_main(int argc UNUSED_PARAM, char **argv)
    224196{
    225197    enum {
     
    230202    char *fromhost;
    231203    char username[USERNAME_SIZE];
    232     const char *tmp;
    233     int amroot;
     204    const char *shell;
     205    int run_by_root;
    234206    unsigned opt;
    235207    int count = 0;
    236208    struct passwd *pw;
    237209    char *opt_host = NULL;
    238     char *opt_user = NULL;
    239     char full_tty[TTYNAME_SIZE];
    240     USE_SELINUX(security_context_t user_sid = NULL;)
    241     USE_FEATURE_UTMP(struct utmp utent;)
    242     USE_PAM(pam_handle_t *pamh;)
    243     USE_PAM(int pamret;)
    244     USE_PAM(const char *failed_msg;)
    245 
    246     short_tty = full_tty;
     210    char *opt_user = opt_user; /* for compiler */
     211    char *full_tty;
     212    IF_SELINUX(security_context_t user_sid = NULL;)
     213#if ENABLE_PAM
     214    int pamret;
     215    pam_handle_t *pamh;
     216    const char *pamuser;
     217    const char *failed_msg;
     218    struct passwd pwdstruct;
     219    char pwdbuf[256];
     220#endif
     221
    247222    username[0] = '\0';
    248     amroot = (getuid() == 0);
    249223    signal(SIGALRM, alarm_handler);
    250224    alarm(TIMEOUT);
     225
     226    /* More of suid paranoia if called by non-root: */
     227    /* Clear dangerous stuff, set PATH */
     228    run_by_root = !sanitize_env_if_suid();
    251229
    252230    /* Mandatory paranoia for suid applet:
     
    258236    opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
    259237    if (opt & LOGIN_OPT_f) {
    260         if (!amroot)
     238        if (!run_by_root)
    261239            bb_error_msg_and_die("-f is for root only");
    262240        safe_strncpy(username, opt_user, sizeof(username));
    263241    }
    264     if (optind < argc) /* user from command line (getty) */
    265         safe_strncpy(username, argv[optind], sizeof(username));
     242    argv += optind;
     243    if (argv[0]) /* user from command line (getty) */
     244        safe_strncpy(username, argv[0], sizeof(username));
    266245
    267246    /* Let's find out and memorize our tty */
    268     if (!isatty(0) || !isatty(1) || !isatty(2))
    269         return EXIT_FAILURE;        /* Must be a terminal */
    270     safe_strncpy(full_tty, "UNKNOWN", sizeof(full_tty));
    271     tmp = ttyname(0);
    272     if (tmp) {
    273         safe_strncpy(full_tty, tmp, sizeof(full_tty));
    274         if (strncmp(full_tty, "/dev/", 5) == 0)
    275             short_tty = full_tty + 5;
    276     }
    277 
    278     read_or_build_utent(&utent, !amroot);
     247    if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO))
     248        return EXIT_FAILURE;  /* Must be a terminal */
     249    full_tty = xmalloc_ttyname(STDIN_FILENO);
     250    if (!full_tty)
     251        full_tty = xstrdup("UNKNOWN");
     252    short_tty = skip_dev_pfx(full_tty);
    279253
    280254    if (opt_host) {
    281         USE_FEATURE_UTMP(
    282             safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));
    283         )
    284255        fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
    285     } else
     256    } else {
    286257        fromhost = xasprintf(" on '%s'", short_tty);
     258    }
    287259
    288260    /* Was breaking "login <username>" from shell command line: */
    289261    /*bb_setpgrp();*/
    290262
    291     openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
     263    openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH);
    292264
    293265    while (1) {
     266        /* flush away any type-ahead (as getty does) */
     267        tcflush(0, TCIFLUSH);
     268
    294269        if (!username[0])
    295270            get_username_or_die(username, sizeof(username));
     
    298273        pamret = pam_start("login", username, &conv, &pamh);
    299274        if (pamret != PAM_SUCCESS) {
    300             failed_msg = "pam_start";
     275            failed_msg = "start";
    301276            goto pam_auth_failed;
    302277        }
     
    304279        pamret = pam_set_item(pamh, PAM_TTY, short_tty);
    305280        if (pamret != PAM_SUCCESS) {
    306             failed_msg = "pam_set_item(TTY)";
     281            failed_msg = "set_item(TTY)";
    307282            goto pam_auth_failed;
    308283        }
    309284        pamret = pam_authenticate(pamh, 0);
    310285        if (pamret != PAM_SUCCESS) {
    311             failed_msg = "pam_authenticate";
     286            failed_msg = "authenticate";
    312287            goto pam_auth_failed;
    313288            /* TODO: or just "goto auth_failed"
     
    319294        pamret = pam_acct_mgmt(pamh, 0);
    320295        if (pamret != PAM_SUCCESS) {
    321             failed_msg = "account setup";
     296            failed_msg = "acct_mgmt";
    322297            goto pam_auth_failed;
    323298        }
    324299        /* read user back */
    325         {
    326             const char *pamuser;
    327             /* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
    328              * thus we cast to (void*) */
    329             if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
    330                 failed_msg = "pam_get_item(USER)";
    331                 goto pam_auth_failed;
    332             }
    333             safe_strncpy(username, pamuser, sizeof(username));
    334         }
    335         /* If we get here, the user was authenticated, and is
    336          * granted access. */
    337         pw = getpwnam(username);
    338         if (pw)
    339             break;
    340         goto auth_failed;
     300        pamuser = NULL;
     301        /* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
     302         * thus we cast to (void*) */
     303        if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
     304            failed_msg = "get_item(USER)";
     305            goto pam_auth_failed;
     306        }
     307        if (!pamuser || !pamuser[0])
     308            goto auth_failed;
     309        safe_strncpy(username, pamuser, sizeof(username));
     310        /* Don't use "pw = getpwnam(username);",
     311         * PAM is said to be capable of destroying static storage
     312         * used by getpwnam(). We are using safe(r) function */
     313        pw = NULL;
     314        getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw);
     315        if (!pw)
     316            goto auth_failed;
     317        pamret = pam_open_session(pamh, 0);
     318        if (pamret != PAM_SUCCESS) {
     319            failed_msg = "open_session";
     320            goto pam_auth_failed;
     321        }
     322        pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
     323        if (pamret != PAM_SUCCESS) {
     324            failed_msg = "setcred";
     325            goto pam_auth_failed;
     326        }
     327        break; /* success, continue login process */
     328
    341329 pam_auth_failed:
    342         bb_error_msg("%s failed: %s (%d)", failed_msg, pam_strerror(pamh, pamret), pamret);
     330        /* syslog, because we don't want potential attacker
     331         * to know _why_ login failed */
     332        syslog(LOG_WARNING, "pam_%s call failed: %s (%d)", failed_msg,
     333                    pam_strerror(pamh, pamret), pamret);
    343334        safe_strncpy(username, "UNKNOWN", sizeof(username));
    344335#else /* not PAM */
     
    374365            syslog(LOG_WARNING, "invalid password for '%s'%s",
    375366                        username, fromhost);
     367
     368            if (ENABLE_FEATURE_CLEAN_UP)
     369                free(fromhost);
     370
    376371            return EXIT_FAILURE;
    377372        }
    378373        username[0] = '\0';
    379     }
     374    } /* while (1) */
    380375
    381376    alarm(0);
    382     die_if_nologin_and_non_root(pw->pw_uid == 0);
    383 
    384     write_utent(&utent, username);
    385 
    386 #if ENABLE_SELINUX
    387     if (is_selinux_enabled()) {
    388         security_context_t old_tty_sid, new_tty_sid;
    389 
    390         if (get_default_context(username, NULL, &user_sid)) {
    391             bb_error_msg_and_die("cannot get SID for %s",
    392                     username);
    393         }
    394         if (getfilecon(full_tty, &old_tty_sid) < 0) {
    395             bb_perror_msg_and_die("getfilecon(%s) failed",
    396                     full_tty);
    397         }
    398         if (security_compute_relabel(user_sid, old_tty_sid,
    399                     SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
    400             bb_perror_msg_and_die("security_change_sid(%s) failed",
    401                     full_tty);
    402         }
    403         if (setfilecon(full_tty, new_tty_sid) != 0) {
    404             bb_perror_msg_and_die("chsid(%s, %s) failed",
    405                     full_tty, new_tty_sid);
    406         }
    407     }
    408 #endif
     377    /* We can ignore /etc/nologin if we are logging in as root,
     378     * it doesn't matter whether we are run by root or not */
     379    if (pw->pw_uid != 0)
     380        die_if_nologin();
     381
     382    IF_SELINUX(initselinux(username, full_tty, &user_sid));
     383
    409384    /* Try these, but don't complain if they fail.
    410385     * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
     
    412387    fchmod(0, 0600);
    413388
    414     if (ENABLE_LOGIN_SCRIPTS) {
    415         char *t_argv[2];
    416 
    417         t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
    418         if (t_argv[0]) {
    419             t_argv[1] = NULL;
    420             xsetenv("LOGIN_TTY", full_tty);
    421             xsetenv("LOGIN_USER", pw->pw_name);
    422             xsetenv("LOGIN_UID", utoa(pw->pw_uid));
    423             xsetenv("LOGIN_GID", utoa(pw->pw_gid));
    424             xsetenv("LOGIN_SHELL", pw->pw_shell);
    425             xspawn(t_argv); /* NOMMU-friendly */
    426             /* All variables are unset by setup_environment */
    427             wait(NULL);
    428         }
    429     }
     389    update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL);
     390
     391    /* We trust environment only if we run by root */
     392    if (ENABLE_LOGIN_SCRIPTS && run_by_root)
     393        run_login_script(pw, full_tty);
    430394
    431395    change_identity(pw);
    432     tmp = pw->pw_shell;
    433     if (!tmp || !*tmp)
    434         tmp = DEFAULT_SHELL;
    435     /* setup_environment params: shell, loginshell, changeenv, pw */
    436     setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw);
    437     /* FIXME: login shell = 1 -> 3rd parameter is ignored! */
     396    shell = pw->pw_shell;
     397    if (!shell || !shell[0])
     398        shell = DEFAULT_SHELL;
     399    setup_environment(shell,
     400            (!(opt & LOGIN_OPT_p) * SETUP_ENV_CLEARENV) + SETUP_ENV_CHANGEENV,
     401            pw);
    438402
    439403    motd();
     
    441405    if (pw->pw_uid == 0)
    442406        syslog(LOG_INFO, "root login%s", fromhost);
    443 #if ENABLE_SELINUX
     407
     408    if (ENABLE_FEATURE_CLEAN_UP)
     409        free(fromhost);
     410
    444411    /* well, a simple setexeccon() here would do the job as well,
    445412     * but let's play the game for now */
    446     set_current_security_context(user_sid);
    447 #endif
     413    IF_SELINUX(set_current_security_context(user_sid);)
    448414
    449415    // util-linux login also does:
     
    456422    // If this stuff is really needed, add it and explain why!
    457423
    458     /* set signals to defaults */
    459     signal(SIGALRM, SIG_DFL);
     424    /* Set signals to defaults */
     425    /* Non-ignored signals revert to SIG_DFL on exec anyway */
     426    /*signal(SIGALRM, SIG_DFL);*/
     427
    460428    /* Is this correct? This way user can ctrl-c out of /etc/profile,
    461429     * potentially creating security breach (tested with bash 3.0).
     
    463431     * Maybe bash is buggy?
    464432     * Need to find out what standards say about /bin/login -
    465      * should it leave SIGINT etc enabled or disabled? */
     433     * should we leave SIGINT etc enabled or disabled? */
    466434    signal(SIGINT, SIG_DFL);
    467435
    468436    /* Exec login shell with no additional parameters */
    469     run_shell(tmp, 1, NULL, NULL);
     437    run_shell(shell, 1, NULL, NULL);
    470438
    471439    /* return EXIT_FAILURE; - not reached */
  • branches/2.2.9/mindi-busybox/loginutils/passwd.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    44 */
    5 
    65#include "libbb.h"
    76#include <syslog.h>
    8 
    97
    108static void nuke_str(char *str)
     
    1816    char *orig = (char*)"";
    1917    char *newp = NULL;
    20     char *cipher = NULL;
    2118    char *cp = NULL;
    2219    char *ret = NULL; /* failure so far */
    2320
    2421    if (myuid && pw->pw_passwd[0]) {
    25         orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
     22        char *encrypted;
     23
     24        orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
    2625        if (!orig)
    2726            goto err_ret;
    28         cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */
    29         if (strcmp(cipher, pw->pw_passwd) != 0) {
    30             syslog(LOG_WARNING, "incorrect password for '%s'",
     27        encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
     28        if (strcmp(encrypted, pw->pw_passwd) != 0) {
     29            syslog(LOG_WARNING, "incorrect password for %s",
    3130                pw->pw_name);
    3231            bb_do_delay(FAIL_DELAY);
     
    3433            goto err_ret;
    3534        }
    36     }
    37     orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
    38     newp = bb_askpass(0, "New password:"); /* returns ptr to static */
     35        if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
     36    }
     37    orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
     38    newp = bb_ask_stdin("New password: "); /* returns ptr to static */
    3939    if (!newp)
    4040        goto err_ret;
    41     newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */
     41    newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
    4242    if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
    4343     && obscure(orig, newp, pw) && myuid)
    4444        goto err_ret; /* non-root is not allowed to have weak passwd */
    4545
    46     cp = bb_askpass(0, "Retype password:");
     46    cp = bb_ask_stdin("Retype password: ");
    4747    if (!cp)
    4848        goto err_ret;
     
    5757        crypt_make_salt(salt + 3, 4, 0);
    5858    }
    59     /* pw_encrypt returns ptr to static */
    60     ret = xstrdup(pw_encrypt(newp, salt));
     59    /* pw_encrypt returns malloced str */
     60    ret = pw_encrypt(newp, salt, 1);
    6161    /* whee, success! */
    6262
     
    6666    nuke_str(newp);
    6767    if (ENABLE_FEATURE_CLEAN_UP) free(newp);
    68     nuke_str(cipher);
    6968    nuke_str(cp);
    7069    return ret;
    7170}
    7271
    73 int passwd_main(int argc, char **argv);
    74 int passwd_main(int argc, char **argv)
     72int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     73int passwd_main(int argc UNUSED_PARAM, char **argv)
    7574{
    7675    enum {
     
    9493    struct rlimit rlimit_fsize;
    9594    char c;
    96 
    9795#if ENABLE_FEATURE_SHADOWPASSWDS
    9896    /* Using _r function to avoid pulling in static buffers */
    9997    struct spwd spw;
    100     struct spwd *result;
    10198    char buffer[256];
    10299#endif
    103100
    104101    logmode = LOGMODE_BOTH;
    105     openlog(applet_name, LOG_NOWAIT, LOG_AUTH);
     102    openlog(applet_name, 0, LOG_AUTH);
    106103    opt = getopt32(argv, "a:lud", &opt_a);
    107104    //argc -= optind;
     
    118115
    119116    /* Will complain and die if username not found */
    120     myname = xstrdup(bb_getpwuid(NULL, -1, myuid));
     117    myname = xstrdup(xuid2uname(myuid));
    121118    name = argv[0] ? argv[0] : myname;
    122119
    123     pw = getpwnam(name);
    124     if (!pw) bb_error_msg_and_die("unknown user %s", name);
     120    pw = xgetpwnam(name);
    125121    if (myuid && pw->pw_uid != myuid) {
    126122        /* LOGMODE_BOTH */
     
    129125
    130126#if ENABLE_FEATURE_SHADOWPASSWDS
    131     /* getspnam_r() can lie! Even if user isn't in shadow, it can
    132      * return success (pwd field was seen set to "!" in this case) */
    133     if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)
    134      || LONE_CHAR(spw.sp_pwdp, '!')) {
    135         /* LOGMODE_BOTH */
    136         bb_error_msg("no record of %s in %s, using %s",
    137                 name, bb_path_shadow_file,
    138                 bb_path_passwd_file);
    139     } else {
    140         pw->pw_passwd = spw.sp_pwdp;
     127    {
     128        /* getspnam_r may return 0 yet set result to NULL.
     129         * At least glibc 2.4 does this. Be extra paranoid here. */
     130        struct spwd *result = NULL;
     131        errno = 0;
     132        if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
     133         || !result /* no error, but no record found either */
     134         || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
     135        ) {
     136            if (errno != ENOENT) {
     137                /* LOGMODE_BOTH */
     138                bb_perror_msg("no record of %s in %s, using %s",
     139                    name, bb_path_shadow_file,
     140                    bb_path_passwd_file);
     141            }
     142            /* else: /etc/shadow does not exist,
     143             * apparently we are on a shadow-less system,
     144             * no surprise there */
     145        } else {
     146            pw->pw_passwd = result->sp_pwdp;
     147        }
    141148    }
    142149#endif
     
    148155        if (myuid && !c) { /* passwd starts with '!' */
    149156            /* LOGMODE_BOTH */
    150             bb_error_msg_and_die("cannot change "
     157            bb_error_msg_and_die("can't change "
    151158                    "locked password for %s", name);
    152159        }
     
    162169    } else if (opt & OPT_unlock) {
    163170        if (c) goto skip; /* not '!' */
    164         /* pw->pw_passwd pints to static storage,
     171        /* pw->pw_passwd points to static storage,
    165172         * strdup'ing to avoid nasty surprizes */
    166173        newp = xstrdup(&pw->pw_passwd[1]);
     
    172179    rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
    173180    setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
    174     signal(SIGHUP, SIG_IGN);
    175     signal(SIGINT, SIG_IGN);
    176     signal(SIGQUIT, SIG_IGN);
     181    bb_signals(0
     182        + (1 << SIGHUP)
     183        + (1 << SIGINT)
     184        + (1 << SIGQUIT)
     185        , SIG_IGN);
    177186    umask(077);
    178187    xsetuid(0);
     
    180189#if ENABLE_FEATURE_SHADOWPASSWDS
    181190    filename = bb_path_shadow_file;
    182     rc = update_passwd(bb_path_shadow_file, name, newp);
     191    rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
    183192    if (rc == 0) /* no lines updated, no errors detected */
    184193#endif
    185194    {
    186195        filename = bb_path_passwd_file;
    187         rc = update_passwd(bb_path_passwd_file, name, newp);
     196        rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
    188197    }
    189198    /* LOGMODE_BOTH */
    190199    if (rc < 0)
    191         bb_error_msg_and_die("cannot update password file %s",
     200        bb_error_msg_and_die("can't update password file %s",
    192201                filename);
    193202    bb_info_msg("Password for %s changed by %s", name, myname);
  • branches/2.2.9/mindi-busybox/loginutils/su.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  *  Mini su implementation for busybox
     3 * Mini su implementation for busybox
    44 *
    5  *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    66 */
    77
     
    99#include <syslog.h>
    1010
     11#if ENABLE_FEATURE_SU_CHECKS_SHELLS
     12/* Return 1 if SHELL is a restricted shell (one not returned by
     13 * getusershell), else 0, meaning it is a standard shell.  */
     14static int restricted_shell(const char *shell)
     15{
     16    char *line;
     17    int result = 1;
     18
     19    /*setusershell(); - getusershell does it itself*/
     20    while ((line = getusershell()) != NULL) {
     21        if (/* *line != '#' && */ strcmp(line, shell) == 0) {
     22            result = 0;
     23            break;
     24        }
     25    }
     26    if (ENABLE_FEATURE_CLEAN_UP)
     27        endusershell();
     28    return result;
     29}
     30#endif
     31
    1132#define SU_OPT_mp (3)
    12 #define SU_OPT_l (4)
     33#define SU_OPT_l  (4)
    1334
    14 int su_main(int argc, char **argv);
    15 int su_main(int argc, char **argv)
     35int su_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     36int su_main(int argc UNUSED_PARAM, char **argv)
    1637{
    1738    unsigned flags;
     
    2243    uid_t cur_uid = getuid();
    2344    const char *tty;
    24     char *old_user;
     45    char user_buf[64];
     46    const char *old_user;
    2547
    2648    flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
    27     argc -= optind;
     49    //argc -= optind;
    2850    argv += optind;
    2951
    30     if (argc && LONE_DASH(argv[0])) {
     52    if (argv[0] && LONE_DASH(argv[0])) {
    3153        flags |= SU_OPT_l;
    32         argc--;
    3354        argv++;
    3455    }
    3556
    3657    /* get user if specified */
    37     if (argc) {
     58    if (argv[0]) {
    3859        opt_username = argv[0];
    39 //      argc--;
    4060        argv++;
    4161    }
    4262
    4363    if (ENABLE_FEATURE_SU_SYSLOG) {
    44         /* The utmp entry (via getlogin) is probably the best way to identify
    45         the user, especially if someone su's from a su-shell.
    46         But getlogin can fail -- usually due to lack of utmp entry.
    47         in this case resort to getpwuid.  */
    48         old_user = xstrdup(USE_FEATURE_UTMP(getlogin() ? : ) (pw = getpwuid(cur_uid)) ? pw->pw_name : "");
    49         tty = ttyname(2) ? : "none";
     64        /* The utmp entry (via getlogin) is probably the best way to
     65         * identify the user, especially if someone su's from a su-shell.
     66         * But getlogin can fail -- usually due to lack of utmp entry.
     67         * in this case resort to getpwuid.  */
     68#if ENABLE_FEATURE_UTMP
     69        old_user = user_buf;
     70        if (getlogin_r(user_buf, sizeof(user_buf)) != 0)
     71#endif
     72        {
     73            pw = getpwuid(cur_uid);
     74            old_user = pw ? xstrdup(pw->pw_name) : "";
     75        }
     76        tty = xmalloc_ttyname(2);
     77        if (!tty) {
     78            tty = "none";
     79        }
    5080        openlog(applet_name, 0, LOG_AUTH);
    5181    }
    5282
    53     pw = getpwnam(opt_username);
    54     if (!pw)
    55         bb_error_msg_and_die("unknown id: %s", opt_username);
     83    pw = xgetpwnam(opt_username);
    5684
    57     /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
    58        is a username that is retrieved via NIS (YP), but that doesn't have
    59        a default shell listed.  */
    60     if (!pw->pw_shell || !pw->pw_shell[0])
    61         pw->pw_shell = (char *)DEFAULT_SHELL;
    62 
    63     if ((cur_uid == 0) || correct_password(pw)) {
     85    if (cur_uid == 0 || correct_password(pw)) {
    6486        if (ENABLE_FEATURE_SU_SYSLOG)
    6587            syslog(LOG_NOTICE, "%c %s %s:%s",
     
    7496    if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
    7597        closelog();
    76         free(old_user);
    7798    }
    7899
    79     if (!opt_shell && (flags & SU_OPT_mp))
     100    if (!opt_shell && (flags & SU_OPT_mp)) {
     101        /* -s SHELL is not given, but "preserve env" opt is */
    80102        opt_shell = getenv("SHELL");
     103    }
     104
     105    /* Make sure pw->pw_shell is non-NULL.  It may be NULL when NEW_USER
     106     * is a username that is retrieved via NIS (YP), that doesn't have
     107     * a default shell listed.  */
     108    if (!pw->pw_shell || !pw->pw_shell[0])
     109        pw->pw_shell = (char *)DEFAULT_SHELL;
    81110
    82111#if ENABLE_FEATURE_SU_CHECKS_SHELLS
    83     if (opt_shell && cur_uid && restricted_shell(pw->pw_shell)) {
     112    if (opt_shell && cur_uid != 0 && restricted_shell(pw->pw_shell)) {
    84113        /* The user being su'd to has a nonstandard shell, and so is
    85            probably a uucp account or has restricted access.  Don't
    86            compromise the account by allowing access with a standard
    87            shell.  */
     114         * probably a uucp account or has restricted access.  Don't
     115         * compromise the account by allowing access with a standard
     116         * shell.  */
    88117        bb_error_msg("using restricted shell");
    89         opt_shell = 0;
     118        opt_shell = NULL;
    90119    }
     120    /* else: user can run whatever he wants via "su -s PROG USER".
     121     * This is safe since PROG is run under user's uid/gid. */
    91122#endif
    92123    if (!opt_shell)
     
    94125
    95126    change_identity(pw);
    96     setup_environment(opt_shell, flags & SU_OPT_l, !(flags & SU_OPT_mp), pw);
    97     USE_SELINUX(set_current_security_context(NULL);)
     127    setup_environment(opt_shell,
     128            ((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV)
     129            + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
     130            pw);
     131    IF_SELINUX(set_current_security_context(NULL);)
    98132
    99133    /* Never returns */
    100134    run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
    101135
    102     return EXIT_FAILURE;
     136    /* return EXIT_FAILURE; - not reached */
    103137}
  • branches/2.2.9/mindi-busybox/loginutils/sulogin.c

    r1765 r2725  
    33 * Mini sulogin implementation for busybox
    44 *
    5  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    66 */
    77
     8#include "libbb.h"
    89#include <syslog.h>
    910
    10 #include "libbb.h"
    11 
    12 static const char *const forbid[] = {
    13     "ENV",
    14     "BASH_ENV",
    15     "HOME",
    16     "IFS",
    17     "PATH",
    18     "SHELL",
    19     "LD_LIBRARY_PATH",
    20     "LD_PRELOAD",
    21     "LD_TRACE_LOADED_OBJECTS",
    22     "LD_BIND_NOW",
    23     "LD_AOUT_LIBRARY_PATH",
    24     "LD_AOUT_PRELOAD",
    25     "LD_NOWARN",
    26     "LD_KEEPDIR",
    27     (char *) 0
    28 };
     11//static void catchalarm(int UNUSED_PARAM junk)
     12//{
     13//  exit(EXIT_FAILURE);
     14//}
    2915
    3016
    31 static void catchalarm(int ATTRIBUTE_UNUSED junk)
    32 {
    33     exit(EXIT_FAILURE);
    34 }
    35 
    36 
    37 int sulogin_main(int argc, char **argv);
    38 int sulogin_main(int argc, char **argv)
     17int sulogin_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     18int sulogin_main(int argc UNUSED_PARAM, char **argv)
    3919{
    4020    char *cp;
    4121    int timeout = 0;
    42     char *timeout_arg;
    43     const char *const *p;
    4422    struct passwd *pwd;
    4523    const char *shell;
     
    4826    char buffer[256];
    4927    struct spwd spw;
    50     struct spwd *result;
    5128#endif
    5229
     
    5431    openlog(applet_name, 0, LOG_AUTH);
    5532
    56     if (getopt32(argv, "t:", &timeout_arg)) {
    57         timeout = xatoi_u(timeout_arg);
    58     }
     33    opt_complementary = "t+"; /* -t N */
     34    getopt32(argv, "t:", &timeout);
     35    argv += optind;
    5936
    60     if (argv[optind]) {
     37    if (argv[0]) {
    6138        close(0);
    6239        close(1);
    63         dup(xopen(argv[optind], O_RDWR));
     40        dup(xopen(argv[0], O_RDWR));
    6441        close(2);
    6542        dup(0);
    6643    }
    6744
     45    /* Malicious use like "sulogin /dev/sda"? */
    6846    if (!isatty(0) || !isatty(1) || !isatty(2)) {
    6947        logmode = LOGMODE_SYSLOG;
     
    7149    }
    7250
    73     /* Clear out anything dangerous from the environment */
    74     for (p = forbid; *p; p++)
    75         unsetenv(*p);
    76 
    77     signal(SIGALRM, catchalarm);
     51    /* Clear dangerous stuff, set PATH */
     52    sanitize_env_if_suid();
    7853
    7954    pwd = getpwuid(0);
     
    8358
    8459#if ENABLE_FEATURE_SHADOWPASSWDS
    85     if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) {
    86         goto auth_error;
     60    {
     61        /* getspnam_r may return 0 yet set result to NULL.
     62         * At least glibc 2.4 does this. Be extra paranoid here. */
     63        struct spwd *result = NULL;
     64        int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result);
     65        if (r || !result) {
     66            goto auth_error;
     67        }
     68        pwd->pw_passwd = result->sp_pwdp;
    8769    }
    88     pwd->pw_passwd = spw.sp_pwdp;
    8970#endif
    9071
    9172    while (1) {
     73        char *encrypted;
     74        int r;
     75
    9276        /* cp points to a static buffer that is zeroed every time */
    93         cp = bb_askpass(timeout,
     77        cp = bb_ask(STDIN_FILENO, timeout,
    9478                "Give root password for system maintenance\n"
    9579                "(or type Control-D for normal startup):");
     
    9983            return 0;
    10084        }
    101         if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) {
     85        encrypted = pw_encrypt(cp, pwd->pw_passwd, 1);
     86        r = strcmp(encrypted, pwd->pw_passwd);
     87        free(encrypted);
     88        if (r == 0) {
    10289            break;
    10390        }
     
    10693    }
    10794    memset(cp, 0, strlen(cp));
    108     signal(SIGALRM, SIG_DFL);
     95//  signal(SIGALRM, SIG_DFL);
    10996
    11097    bb_info_msg("System Maintenance Mode");
    11198
    112     USE_SELINUX(renew_current_security_context());
     99    IF_SELINUX(renew_current_security_context());
    113100
    114101    shell = getenv("SUSHELL");
    115     if (!shell) shell = getenv("sushell");
    116     if (!shell) {
    117         shell = "/bin/sh";
    118         if (pwd->pw_shell[0])
    119             shell = pwd->pw_shell;
    120     }
    121     run_shell(shell, 1, 0, 0);
    122     /* never returns */
     102    if (!shell)
     103        shell = getenv("sushell");
     104    if (!shell)
     105        shell = pwd->pw_shell;
    123106
    124 auth_error:
    125     bb_error_msg_and_die("no password entry for 'root'");
     107    /* Exec login shell with no additional parameters. Never returns. */
     108    run_shell(shell, 1, NULL, NULL);
     109
     110 auth_error:
     111    bb_error_msg_and_die("no password entry for root");
    126112}
  • branches/2.2.9/mindi-busybox/loginutils/vlock.c

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    2 
    32/*
    43 * vlock implementation for busybox
     
    76 * Written by spoon <spon@ix.netcom.com>
    87 *
    9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    109 */
    1110
     
    1817
    1918#include "libbb.h"
     19
     20#ifdef __linux__
    2021#include <sys/vt.h>
    2122
    22 static struct passwd *pw;
    23 static struct vt_mode ovtm;
    24 static struct termios oterm;
    25 static int vfd;
    26 static unsigned long o_lock_all;
    27 
    28 static void release_vt(int signo)
     23static void release_vt(int signo UNUSED_PARAM)
    2924{
    30     ioctl(vfd, VT_RELDISP, !o_lock_all);
     25    /* If -a, param is 0, which means:
     26     * "no, kernel, we don't allow console switch away from us!" */
     27    ioctl(STDIN_FILENO, VT_RELDISP, (unsigned long) !option_mask32);
    3128}
    3229
    33 static void acquire_vt(int signo)
     30static void acquire_vt(int signo UNUSED_PARAM)
    3431{
    35     ioctl(vfd, VT_RELDISP, VT_ACKACQ);
     32    /* ACK to kernel that switch to console is successful */
     33    ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
    3634}
     35#endif
    3736
    38 static void restore_terminal(void)
     37int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     38int vlock_main(int argc UNUSED_PARAM, char **argv)
    3939{
    40     ioctl(vfd, VT_SETMODE, &ovtm);
    41     tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
    42 }
     40#ifdef __linux__
     41    struct vt_mode vtm;
     42    struct vt_mode ovtm;
     43#endif
     44    struct termios term;
     45    struct termios oterm;
     46    struct passwd *pw;
    4347
    44 int vlock_main(int argc, char **argv);
    45 int vlock_main(int argc, char **argv)
    46 {
    47     sigset_t sig;
    48     struct sigaction sa;
    49     struct vt_mode vtm;
    50     struct termios term;
    51     uid_t uid = getuid();
     48    pw = xgetpwuid(getuid());
     49    opt_complementary = "=0"; /* no params! */
     50    getopt32(argv, "a");
    5251
    53     pw = getpwuid(uid);
    54     if (pw == NULL)
    55         bb_error_msg_and_die("unknown uid %d", uid);
     52    /* Ignore some signals so that we don't get killed by them */
     53    bb_signals(0
     54        + (1 << SIGTSTP)
     55        + (1 << SIGTTIN)
     56        + (1 << SIGTTOU)
     57        + (1 << SIGHUP )
     58        + (1 << SIGCHLD) /* paranoia :) */
     59        + (1 << SIGQUIT)
     60        + (1 << SIGINT )
     61        , SIG_IGN);
    5662
    57     if (argc > 2) {
    58         bb_show_usage();
    59     }
     63#ifdef __linux__
     64    /* We will use SIGUSRx for console switch control: */
     65    /* 1: set handlers */
     66    signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
     67    signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt);
     68    /* 2: unmask them */
     69    sig_unblock(SIGUSR1);
     70    sig_unblock(SIGUSR2);
     71#endif
    6072
    61     o_lock_all = getopt32(argv, "a");
     73    /* Revert stdin/out to our controlling tty
     74     * (or die if we have none) */
     75    xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
     76    xdup2(STDIN_FILENO, STDOUT_FILENO);
    6277
    63     vfd = xopen(CURRENT_TTY, O_RDWR);
    64 
    65     xioctl(vfd, VT_GETMODE, &vtm);
    66 
    67     /* mask a bunch of signals */
    68     sigprocmask(SIG_SETMASK, NULL, &sig);
    69     sigdelset(&sig, SIGUSR1);
    70     sigdelset(&sig, SIGUSR2);
    71     sigaddset(&sig, SIGTSTP);
    72     sigaddset(&sig, SIGTTIN);
    73     sigaddset(&sig, SIGTTOU);
    74     sigaddset(&sig, SIGHUP);
    75     sigaddset(&sig, SIGCHLD);
    76     sigaddset(&sig, SIGQUIT);
    77     sigaddset(&sig, SIGINT);
    78 
    79     sigemptyset(&(sa.sa_mask));
    80     sa.sa_flags = SA_RESTART;
    81     sa.sa_handler = release_vt;
    82     sigaction(SIGUSR1, &sa, NULL);
    83     sa.sa_handler = acquire_vt;
    84     sigaction(SIGUSR2, &sa, NULL);
    85 
    86     /* need to handle some signals so that we don't get killed by them */
    87     sa.sa_handler = SIG_IGN;
    88     sigaction(SIGHUP, &sa, NULL);
    89     sigaction(SIGQUIT, &sa, NULL);
    90     sigaction(SIGINT, &sa, NULL);
    91     sigaction(SIGTSTP, &sa, NULL);
    92 
     78#ifdef __linux__
     79    xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
    9380    ovtm = vtm;
     81    /* "console switches are controlled by us, not kernel!" */
    9482    vtm.mode = VT_PROCESS;
    9583    vtm.relsig = SIGUSR1;
    9684    vtm.acqsig = SIGUSR2;
    97     ioctl(vfd, VT_SETMODE, &vtm);
     85    ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
     86#endif
    9887
    9988    tcgetattr(STDIN_FILENO, &oterm);
     
    10392    term.c_lflag &= ~ISIG;
    10493    term.c_lflag &= ~(ECHO | ECHOCTL);
    105     tcsetattr(STDIN_FILENO, TCSANOW, &term);
     94    tcsetattr_stdin_TCSANOW(&term);
    10695
    10796    do {
    108         printf("Virtual Console%s locked by %s.\n", (o_lock_all) ? "s" : "", pw->pw_name);
     97        printf("Virtual console%s locked by %s.\n",
     98                option_mask32 /*o_lock_all*/ ? "s" : "",
     99                pw->pw_name);
    109100        if (correct_password(pw)) {
    110101            break;
     
    113104        puts("Password incorrect");
    114105    } while (1);
    115     restore_terminal();
    116     fflush_stdout_and_exit(0);
     106
     107#ifdef __linux__
     108    ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
     109#endif
     110    tcsetattr_stdin_TCSANOW(&oterm);
     111    fflush_stdout_and_exit(EXIT_SUCCESS);
    117112}
Note: See TracChangeset for help on using the changeset viewer.