Port over the last of the tinylogin applets -Erik
No binary (build failed)
—
(log yok)
| @@ -335,6 +335,9 @@ | |
| #ifdef CONFIG_OD | |
| APPLET(od, od_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| + | #ifdef CONFIG_PASSWD |
| + | APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) |
| + | #endif |
| #ifdef CONFIG_PIDOF | |
| APPLET(pidof, pidof_main, _BB_DIR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| @@ -419,6 +422,9 @@ | |
| #ifdef CONFIG_SU | |
| APPLET(su, su_main, _BB_DIR_BIN, _BB_SUID_ALWAYS) | |
| #endif | |
| + | #ifdef CONFIG_SULOGIN |
| + | APPLET(sulogin, sulogin_main, _BB_DIR_SBIN, _BB_SUID_NEVER) |
| + | #endif |
| #ifdef CONFIG_SWAPONOFF | |
| APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER) | |
| #endif | |
| @@ -505,6 +511,9 @@ | |
| #ifdef CONFIG_VI | |
| APPLET(vi, vi_main, _BB_DIR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| + | #ifdef CONFIG_VLOCK |
| + | APPLET(vlock, vlock_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) |
| + | #endif |
| #ifdef CONFIG_WATCHDOG | |
| APPLET(watchdog, watchdog_main, _BB_DIR_SBIN, _BB_SUID_NEVER) | |
| #endif | |
| @@ -39,6 +39,16 @@ | |
| #include "config.h" | |
| + | #include "pwd.h" |
| + | #include "grp.h" |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | #include "shadow_.h" |
| + | #endif |
| + | #ifdef CONFIG_FEATURE_SHA1_PASSWORDS |
| + | # include "sha1.h" |
| + | #endif |
| + | |
| + | |
| #if (__GNU_LIBRARY__ < 5) && (!defined __dietlibc__) | |
| /* libc5 doesn't define socklen_t */ | |
| typedef unsigned int socklen_t; | |
| @@ -260,6 +270,15 @@ extern const char * const too_few_args; | |
| extern const char * const name_longer_than_foo; | |
| extern const char * const unknown; | |
| extern const char * const can_not_create_raw_socket; | |
| + | extern const char * const nologin_file; |
| + | extern const char * const passwd_file; |
| + | extern const char * const shadow_file; |
| + | extern const char * const gshadow_file; |
| + | extern const char * const group_file; |
| + | extern const char * const securetty_file; |
| + | extern const char * const motd_file; |
| + | extern const char * const issue_file; |
| + | extern const char * const _path_login; |
| #ifdef CONFIG_FEATURE_DEVFS | |
| # define CURRENT_VC "/dev/vc/0" | |
| @@ -299,4 +318,15 @@ void reset_ino_dev_hashtable(void); | |
| extern size_t xstrlen(const char *string); | |
| #define strlen(x) xstrlen(x) | |
| + | |
| + | #define FAIL_DELAY 3 |
| + | extern void change_identity ( const struct passwd *pw ); |
| + | extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args ); |
| + | extern int restricted_shell ( const char *shell ); |
| + | extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ); |
| + | extern int correct_password ( const struct passwd *pw ); |
| + | extern char *pw_encrypt(const char *clear, const char *salt); |
| + | extern struct spwd *pwd_to_spwd(const struct passwd *pw); |
| + | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp); |
| + | |
| #endif /* __LIBCONFIG_H__ */ | |
| @@ -0,0 +1,82 @@ | |
| + | /* |
| + | * Copyright 1988 - 1994, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #ifndef _H_SHADOW |
| + | #define _H_SHADOW |
| + | |
| + | |
| + | #ifdef USE_SYSTEM_SHADOW |
| + | #include <shadow.h> |
| + | #else |
| + | |
| + | /* |
| + | * This information is not derived from AT&T licensed sources. Posted |
| + | * to the USENET 11/88, and updated 11/90 with information from SVR4. |
| + | * |
| + | * $Id: shadow_.h,v 1.1 2002/06/23 04:24:20 andersen Exp $ |
| + | */ |
| + | |
| + | typedef long sptime; |
| + | |
| + | /* |
| + | * Shadow password security file structure. |
| + | */ |
| + | |
| + | struct spwd { |
| + | char *sp_namp; /* login name */ |
| + | char *sp_pwdp; /* encrypted password */ |
| + | sptime sp_lstchg; /* date of last change */ |
| + | sptime sp_min; /* minimum number of days between changes */ |
| + | sptime sp_max; /* maximum number of days between changes */ |
| + | sptime sp_warn; /* number of days of warning before password |
| + | expires */ |
| + | sptime sp_inact; /* number of days after password expires |
| + | until the account becomes unusable. */ |
| + | sptime sp_expire; /* days since 1/1/70 until account expires */ |
| + | unsigned long sp_flag; /* reserved for future use */ |
| + | }; |
| + | |
| + | /* |
| + | * Shadow password security file functions. |
| + | */ |
| + | |
| + | #include <stdio.h> /* for FILE */ |
| + | |
| + | extern struct spwd *getspent(void); |
| + | extern struct spwd *sgetspent(const char *); |
| + | extern struct spwd *fgetspent(FILE *); |
| + | extern void setspent(void); |
| + | extern void endspent(void); |
| + | extern int putspent(const struct spwd *, FILE *); |
| + | extern struct spwd *getspnam(const char *name); |
| + | extern struct spwd *pwd_to_spwd(const struct passwd *pw); |
| + | |
| + | #endif /* USE_LOCAL_SHADOW */ |
| + | |
| + | #endif /* _H_SHADOW */ |
| @@ -1321,6 +1321,24 @@ | |
| "Write an unambiguous representation, octal bytes by default, of FILE\n"\ | |
| "to standard output. With no FILE, or when FILE is -, read standard input." | |
| + | #ifdef CONFIG_FEATURE_SHA1_PASSWORDS |
| + | #define PASSWORD_ALG_TYPES(a) a |
| + | #else |
| + | #define PASSWORD_ALG_TYPES(a) |
| + | #endif |
| + | #define passwd_trivial_usage \ |
| + | "[OPTION] [name]" |
| + | #define passwd_full_usage \ |
| + | "CChange a user password. If no name is specified,\n" \ |
| + | "changes the password for the current user.\n" \ |
| + | "Options:\n" \ |
| + | "\t-a\tDefine which algorithm shall be used for the password.\n" \ |
| + | "\t\t\t(Choices: des, md5" \ |
| + | CONFIG_FEATURE_SHA1_PASSWORDS(", sha1") \ |
| + | ")\n\t-d\tDelete the password for the specified user account.\n" \ |
| + | "\t-l\tLocks (disables) the specified user account.\n" \ |
| + | "\t-u\tUnlocks (re-enables) the specified user account."; |
| + | |
| #define pidof_trivial_usage \ | |
| "process-name [process-name ...]" | |
| #define pidof_full_usage \ | |
| @@ -1586,6 +1604,15 @@ | |
| "Options:\n" \ | |
| "\t-p\tPreserve environment" | |
| + | #define sulogin_trivial_usage \ |
| + | "[OPTION]... [tty-device]" |
| + | #define sulogin_full_usage \ |
| + | "Single user login\n" \ |
| + | "Options:\n" \ |
| + | "\t-f\tDo not authenticate (user already authenticated)\n" \ |
| + | "\t-h\tName of the remote host for this login.\n" \ |
| + | "\t-p\tPreserve environment." |
| + | |
| #define swapoff_trivial_usage \ | |
| "[OPTION] [DEVICE]" | |
| #define swapoff_full_usage \ | |
| @@ -1956,6 +1983,13 @@ | |
| "Options:\n" \ | |
| "\t-R\tRead-only- do not write to the file." | |
| + | #define vlock_trivial_usage \ |
| + | "[OPTIONS]" |
| + | #define vlock_full_usage \ |
| + | "Lock a virtual terminal. A password is required to unlock\n" \ |
| + | "Options:\n" \ |
| + | "\t-a\tLock all VTs" |
| + | |
| #define watchdog_trivial_usage \ | |
| "DEV" | |
| #define watchdog_full_usage \ | |
| @@ -34,17 +34,21 @@ LIBBB_SRC:= \ | |
| my_getpwuid.c parse_mode.c parse_number.c perror_msg.c perror_msg_and_die.c \ | |
| print_file.c process_escape_sequence.c read_package_field.c recursive_action.c \ | |
| safe_read.c safe_strncpy.c syscalls.c syslog_msg_with_name.c time_string.c \ | |
| - | trim.c unzip.c uncompress.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c xfuncs.c \ |
| + | trim.c unzip.c uncompress.c vdprintf.c verror_msg.c vperror_msg.c wfopen.c \ |
| xgetcwd.c xreadlink.c xregcomp.c interface.c remove_file.c last_char_is.c \ | |
| copyfd.c vherror_msg.c herror_msg.c herror_msg_and_die.c xgethostbyname.c \ | |
| dirname.c make_directory.c create_icmp_socket.c u_signal_names.c arith.c \ | |
| - | simplify_path.c inet_common.c inode_hash.c |
| + | simplify_path.c inet_common.c inode_hash.c obscure.c pwd2spwd.c xfuncs.c \ |
| + | correct_password.c change_identity.c setup_environment.c run_shell.c \ |
| + | pw_encrypt.c restricted_shell.c |
| LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC)) | |
| LIBBB_MSRC:=$(LIBBB_DIR)messages.c | |
| LIBBB_MOBJ:=full_version.o name_too_long.o omitting_directory.o not_a_directory.o \ | |
| memory_exhausted.o invalid_date.o invalid_option.o io_error.o dash_dash_help.o \ | |
| - | write_error.o too_few_args.o name_longer_than_foo.o unknown.o can_not_create_raw_socket.o |
| + | write_error.o too_few_args.o name_longer_than_foo.o unknown.o can_not_create_raw_socket.o \ |
| + | shadow_file.o passwd_file.o group_file.o gshadow_file.o nologin_file.o securetty_file.o \ |
| + | motd_file.o |
| LIBBB_MOBJS=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ)) | |
| libraries-y+=$(LIBBB_DIR)$(LIBBB_AR) | |
| @@ -0,0 +1,54 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1991, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <errno.h> |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <syslog.h> |
| + | #include <ctype.h> |
| + | |
| + | #include "libbb.h" |
| + | |
| + | |
| + | /* Become the user and group(s) specified by PW. */ |
| + | void change_identity ( const struct passwd *pw ) |
| + | { |
| + | if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 ) |
| + | perror_msg_and_die ( "cannot set groups" ); |
| + | endgrent ( ); |
| + | |
| + | if ( setgid ( pw-> pw_gid )) |
| + | perror_msg_and_die ( "cannot set group id" ); |
| + | if ( setuid ( pw->pw_uid )) |
| + | perror_msg_and_die ( "cannot set user id" ); |
| + | } |
| + | |
| @@ -0,0 +1,78 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1991, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <errno.h> |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <syslog.h> |
| + | #include <ctype.h> |
| + | #include <crypt.h> |
| + | |
| + | #include "libbb.h" |
| + | |
| + | |
| + | |
| + | /* Ask the user for a password. |
| + | Return 1 if the user gives the correct password for entry PW, |
| + | 0 if not. Return 1 without asking for a password if run by UID 0 |
| + | or if PW has an empty password. */ |
| + | |
| + | int correct_password ( const struct passwd *pw ) |
| + | { |
| + | char *unencrypted, *encrypted, *correct; |
| + | |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) { |
| + | struct spwd *sp = getspnam ( pw-> pw_name ); |
| + | |
| + | if ( !sp ) |
| + | error_msg_and_die ( "no valid shadow password" ); |
| + | |
| + | correct = sp-> sp_pwdp; |
| + | } |
| + | else |
| + | #endif |
| + | correct = pw-> pw_passwd; |
| + | |
| + | if ( correct == 0 || correct[0] == '\0' ) |
| + | return 1; |
| + | |
| + | unencrypted = getpass ( "Password: " ); |
| + | if ( !unencrypted ) |
| + | { |
| + | fputs ( "getpass: cannot open /dev/tty\n", stderr ); |
| + | return 0; |
| + | } |
| + | encrypted = crypt ( unencrypted, correct ); |
| + | memset ( unencrypted, 0, xstrlen ( unencrypted )); |
| + | return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0; |
| + | } |
| @@ -11,6 +11,24 @@ | |
| #if __GNU_LIBRARY__ < 5 | |
| + | /* |
| + | * Some systems already have updwtmp(). Some don't... This is |
| + | * the updwtmp() implementation from uClibc, Copyright 2002 by |
| + | * Erik Andersen <[email protected]> |
| + | */ |
| + | extern void updwtmp(const char *wtmp_file, const struct utmp *lutmp) |
| + | { |
| + | int fd; |
| + | |
| + | fd = open(wtmp_file, O_APPEND | O_WRONLY, 0); |
| + | if (fd >= 0) { |
| + | if (lockf(fd, F_LOCK, 0)==0) { |
| + | write(fd, (const char *) lutmp, sizeof(struct utmp)); |
| + | lockf(fd, F_ULOCK, 0); |
| + | close(fd); |
| + | } |
| + | } |
| + | } |
| /* Copyright (C) 1991 Free Software Foundation, Inc. | |
| This file is part of the GNU C Library. | |
| @@ -66,3 +66,39 @@ | |
| #ifdef L_can_not_create_raw_socket | |
| const char * const can_not_create_raw_socket = "can`t create raw socket"; | |
| #endif | |
| + | |
| + | #ifdef L_passwd_file |
| + | #define PASSWD_FILE "/etc/passwd" |
| + | const char * const passwd_file = PASSWD_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_shadow_file |
| + | #define SHADOW_FILE "/etc/shadow" |
| + | const char * const shadow_file = SHADOW_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_group_file |
| + | #define GROUP_FILE "/etc/group" |
| + | const char * const group_file = GROUP_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_gshadow_file |
| + | #define GSHADOW_FILE "/etc/gshadow" |
| + | const char * const gshadow_file = GSHADOW_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_nologin_file |
| + | #define NOLOGIN_FILE "/etc/nologin" |
| + | const char * const nologin_file = NOLOGIN_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_securetty_file |
| + | #define SECURETTY_FILE "/etc/securetty" |
| + | const char * const securetty_file = SECURETTY_FILE; |
| + | #endif |
| + | |
| + | #ifdef L_motd_file |
| + | #define MOTD_FILE "/etc/motd" |
| + | const char * const motd_file = MOTD_FILE; |
| + | #endif |
| + | |
| @@ -0,0 +1,246 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1994, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | /* |
| + | * This version of obscure.c contains modifications to support "cracklib" |
| + | * by Alec Muffet ([email protected]). You must obtain the Cracklib |
| + | * library source code for this function to operate. |
| + | */ |
| + | |
| + | #include <stdlib.h> |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #include <ctype.h> |
| + | #include "libbb.h" |
| + | |
| + | /* |
| + | * can't be a palindrome - like `R A D A R' or `M A D A M' |
| + | */ |
| + | |
| + | static int palindrome(const char *old, const char *newval) |
| + | { |
| + | int i, j; |
| + | |
| + | i = strlen(newval); |
| + | |
| + | for (j = 0; j < i; j++) |
| + | if (newval[i - j - 1] != newval[j]) |
| + | return 0; |
| + | |
| + | return 1; |
| + | } |
| + | |
| + | /* |
| + | * more than half of the characters are different ones. |
| + | */ |
| + | |
| + | static int similiar(const char *old, const char *newval) |
| + | { |
| + | int i, j; |
| + | |
| + | for (i = j = 0; newval[i] && old[i]; i++) |
| + | if (strchr(newval, old[i])) |
| + | j++; |
| + | |
| + | if (i >= j * 2) |
| + | return 0; |
| + | |
| + | return 1; |
| + | } |
| + | |
| + | /* |
| + | * a nice mix of characters. |
| + | */ |
| + | |
| + | static int simple(const char *old, const char *newval) |
| + | { |
| + | int digits = 0; |
| + | int uppers = 0; |
| + | int lowers = 0; |
| + | int others = 0; |
| + | int size; |
| + | int i; |
| + | |
| + | for (i = 0; newval[i]; i++) { |
| + | if (isdigit(newval[i])) |
| + | digits++; |
| + | else if (isupper(newval[i])) |
| + | uppers++; |
| + | else if (islower(newval[i])) |
| + | lowers++; |
| + | else |
| + | others++; |
| + | } |
| + | |
| + | /* |
| + | * The scam is this - a password of only one character type |
| + | * must be 8 letters long. Two types, 7, and so on. |
| + | */ |
| + | |
| + | size = 9; |
| + | if (digits) |
| + | size--; |
| + | if (uppers) |
| + | size--; |
| + | if (lowers) |
| + | size--; |
| + | if (others) |
| + | size--; |
| + | |
| + | if (size <= i) |
| + | return 0; |
| + | |
| + | return 1; |
| + | } |
| + | |
| + | static char *str_lower(char *string) |
| + | { |
| + | char *cp; |
| + | |
| + | for (cp = string; *cp; cp++) |
| + | *cp = tolower(*cp); |
| + | return string; |
| + | } |
| + | |
| + | static char *password_check(const char *old, const char *newval, const struct passwd *pwdp) |
| + | { |
| + | char *msg = NULL; |
| + | char *oldmono, *newmono, *wrapped; |
| + | |
| + | if (strcmp(newval, old) == 0) |
| + | return "no change"; |
| + | |
| + | newmono = str_lower(xstrdup(newval)); |
| + | oldmono = str_lower(xstrdup(old)); |
| + | wrapped = (char *) xmalloc(strlen(oldmono) * 2 + 1); |
| + | strcpy(wrapped, oldmono); |
| + | strcat(wrapped, oldmono); |
| + | |
| + | if (palindrome(oldmono, newmono)) |
| + | msg = "a palindrome"; |
| + | |
| + | if (!msg && strcmp(oldmono, newmono) == 0) |
| + | msg = "case changes only"; |
| + | |
| + | if (!msg && similiar(oldmono, newmono)) |
| + | msg = "too similiar"; |
| + | |
| + | if (!msg && simple(old, newval)) |
| + | msg = "too simple"; |
| + | |
| + | if (!msg && strstr(wrapped, newmono)) |
| + | msg = "rotated"; |
| + | |
| + | bzero(newmono, strlen(newmono)); |
| + | bzero(oldmono, strlen(oldmono)); |
| + | bzero(wrapped, strlen(wrapped)); |
| + | free(newmono); |
| + | free(oldmono); |
| + | free(wrapped); |
| + | |
| + | return msg; |
| + | } |
| + | |
| + | static char *obscure_msg(const char *old, const char *newval, const struct passwd *pwdp) |
| + | { |
| + | int maxlen, oldlen, newlen; |
| + | char *new1, *old1, *msg; |
| + | |
| + | oldlen = strlen(old); |
| + | newlen = strlen(newval); |
| + | |
| + | #if 0 /* why not check the password when set for the first time? --marekm */ |
| + | if (old[0] == '\0') |
| + | /* return (1); */ |
| + | return NULL; |
| + | #endif |
| + | |
| + | if (newlen < 5) |
| + | return "too short"; |
| + | |
| + | /* |
| + | * Remaining checks are optional. |
| + | */ |
| + | /* Not for us -- Sean |
| + | *if (!getdef_bool("OBSCURE_CHECKS_ENAB")) |
| + | * return NULL; |
| + | */ |
| + | msg = password_check(old, newval, pwdp); |
| + | if (msg) |
| + | return msg; |
| + | |
| + | /* The traditional crypt() truncates passwords to 8 chars. It is |
| + | possible to circumvent the above checks by choosing an easy |
| + | 8-char password and adding some random characters to it... |
| + | Example: "password$%^&*123". So check it again, this time |
| + | truncated to the maximum length. Idea from npasswd. --marekm */ |
| + | |
| + | maxlen = 8; |
| + | if (oldlen <= maxlen && newlen <= maxlen) |
| + | return NULL; |
| + | |
| + | new1 = (char *) xstrdup(newval); |
| + | old1 = (char *) xstrdup(old); |
| + | if (newlen > maxlen) |
| + | new1[maxlen] = '\0'; |
| + | if (oldlen > maxlen) |
| + | old1[maxlen] = '\0'; |
| + | |
| + | msg = password_check(old1, new1, pwdp); |
| + | |
| + | bzero(new1, newlen); |
| + | bzero(old1, oldlen); |
| + | free(new1); |
| + | free(old1); |
| + | |
| + | return msg; |
| + | } |
| + | |
| + | /* |
| + | * Obscure - see if password is obscure enough. |
| + | * |
| + | * The programmer is encouraged to add as much complexity to this |
| + | * routine as desired. Included are some of my favorite ways to |
| + | * check passwords. |
| + | */ |
| + | |
| + | extern int obscure(const char *old, const char *newval, const struct passwd *pwdp) |
| + | { |
| + | char *msg = obscure_msg(old, newval, pwdp); |
| + | |
| + | /* if (msg) { */ |
| + | if (msg != NULL) { |
| + | printf("Bad password: %s.\n", msg); |
| + | /* return 0; */ |
| + | return 1; |
| + | } |
| + | /* return 1; */ |
| + | return 0; |
| + | } |
| @@ -0,0 +1,48 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Utility routine. |
| + | * |
| + | * Copyright (C) 1999-2002 by Erik Andersen <[email protected]> |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | * General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + | * |
| + | */ |
| + | |
| + | #include <string.h> |
| + | #include <crypt.h> |
| + | #include "libbb.h" |
| + | |
| + | |
| + | extern char *pw_encrypt(const char *clear, const char *salt) |
| + | { |
| + | static char cipher[128]; |
| + | char *cp; |
| + | |
| + | #ifdef CONFIG_FEATURE_SHA1_PASSWORDS |
| + | if (strncmp(salt, "$2$", 3) == 0) { |
| + | return sha1_crypt(clear); |
| + | } |
| + | #endif |
| + | cp = (char *) crypt(clear, salt); |
| + | /* if crypt (a nonstandard crypt) returns a string too large, |
| + | truncate it so we don't overrun buffers and hope there is |
| + | enough security in what's left */ |
| + | if (strlen(cp) > sizeof(cipher)-1) { |
| + | cp[sizeof(cipher)-1] = 0; |
| + | } |
| + | strcpy(cipher, cp); |
| + | return cipher; |
| + | } |
| + | |
| @@ -0,0 +1,73 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1994, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #include <time.h> |
| + | #include <sys/types.h> |
| + | #include "libbb.h" |
| + | |
| + | /* |
| + | * pwd_to_spwd - create entries for new spwd structure |
| + | * |
| + | * pwd_to_spwd() creates a new (struct spwd) containing the |
| + | * information in the pointed-to (struct passwd). |
| + | */ |
| + | #define DAY (24L*3600L) |
| + | #define WEEK (7*DAY) |
| + | #define SCALE DAY |
| + | struct spwd *pwd_to_spwd(const struct passwd *pw) |
| + | { |
| + | static struct spwd sp; |
| + | |
| + | /* |
| + | * Nice, easy parts first. The name and passwd map directly |
| + | * from the old password structure to the new one. |
| + | */ |
| + | sp.sp_namp = pw->pw_name; |
| + | sp.sp_pwdp = pw->pw_passwd; |
| + | |
| + | /* |
| + | * Defaults used if there is no pw_age information. |
| + | */ |
| + | sp.sp_min = 0; |
| + | sp.sp_max = (10000L * DAY) / SCALE; |
| + | sp.sp_lstchg = time((time_t *) 0) / SCALE; |
| + | |
| + | /* |
| + | * These fields have no corresponding information in the password |
| + | * file. They are set to uninitialized values. |
| + | */ |
| + | sp.sp_warn = -1; |
| + | sp.sp_expire = -1; |
| + | sp.sp_inact = -1; |
| + | sp.sp_flag = -1; |
| + | |
| + | return &sp; |
| + | } |
| + | |
| @@ -0,0 +1,57 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1991, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <errno.h> |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <syslog.h> |
| + | #include <ctype.h> |
| + | #include "libbb.h" |
| + | |
| + | |
| + | |
| + | /* Return 1 if SHELL is a restricted shell (one not returned by |
| + | getusershell), else 0, meaning it is a standard shell. */ |
| + | |
| + | int restricted_shell ( const char *shell ) |
| + | { |
| + | char *line; |
| + | |
| + | setusershell ( ); |
| + | while (( line = getusershell ( ))) { |
| + | if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) |
| + | break; |
| + | } |
| + | endusershell ( ); |
| + | return line ? 0 : 1; |
| + | } |
| + | |
| @@ -0,0 +1,81 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Copyright 1989 - 1991, Julianne Frances Haugh <[email protected]> |
| + | * All rights reserved. |
| + | * |
| + | * Redistribution and use in source and binary forms, with or without |
| + | * modification, are permitted provided that the following conditions |
| + | * are met: |
| + | * 1. Redistributions of source code must retain the above copyright |
| + | * notice, this list of conditions and the following disclaimer. |
| + | * 2. Redistributions in binary form must reproduce the above copyright |
| + | * notice, this list of conditions and the following disclaimer in the |
| + | * documentation and/or other materials provided with the distribution. |
| + | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors |
| + | * may be used to endorse or promote products derived from this software |
| + | * without specific prior written permission. |
| + | * |
| + | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND |
| + | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| + | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| + | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE |
| + | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| + | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| + | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| + | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| + | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| + | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| + | * SUCH DAMAGE. |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <errno.h> |
| + | #include <unistd.h> |
| + | #include <string.h> |
| + | #include <stdlib.h> |
| + | #include <syslog.h> |
| + | #include <ctype.h> |
| + | #include "libbb.h" |
| + | |
| + | |
| + | /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. |
| + | If COMMAND is nonzero, pass it to the shell with the -c option. |
| + | If ADDITIONAL_ARGS is nonzero, pass it to the shell as more |
| + | arguments. */ |
| + | |
| + | void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args ) |
| + | { |
| + | const char **args; |
| + | int argno = 1; |
| + | int additional_args_cnt = 0; |
| + | |
| + | for ( args = additional_args; args && *args; args++ ) |
| + | additional_args_cnt++; |
| + | |
| + | if ( additional_args ) |
| + | args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt )); |
| + | else |
| + | args = (const char **) xmalloc (sizeof (char *) * 4 ); |
| + | |
| + | args [0] = get_last_path_component ( xstrdup ( shell )); |
| + | |
| + | if ( loginshell ) { |
| + | char *args0 = xmalloc ( xstrlen ( args [0] ) + 2 ); |
| + | args0 [0] = '-'; |
| + | strcpy ( args0 + 1, args [0] ); |
| + | args [0] = args0; |
| + | } |
| + | |
| + | if ( command ) { |
| + | args [argno++] = "-c"; |
| + | args [argno++] = command; |
| + | } |
| + | if ( additional_args ) { |
| + | for ( ; *additional_args; ++additional_args ) |
| + | args [argno++] = *additional_args; |
| + | } |
| + | args [argno] = 0; |
| + | execv ( shell, (char **) args ); |
| + | perror_msg_and_die ( "cannot run %s", shell ); |
| + | } |
| + | |
| @@ -28,8 +28,6 @@ | |
| * SUCH DAMAGE. | |
| */ | |
| - | #include "busybox.h" |
| - | |
| #include <stdio.h> | |
| #include <errno.h> | |
| #include <unistd.h> | |
| @@ -37,98 +35,19 @@ | |
| #include <stdlib.h> | |
| #include <syslog.h> | |
| #include <ctype.h> | |
| + | #include "libbb.h" |
| - | #include "pwd.h" |
| - | #include "grp.h" |
| - | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| - | #include "shadow.h" |
| - | #endif |
| #define DEFAULT_LOGIN_PATH "/bin:/usr/bin" | |
| #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" | |
| - | |
| static void xsetenv ( const char *key, const char *value ) | |
| { | |
| - | if ( setenv ( key, value, 1 )) |
| - | error_msg_and_die ( "out of memory" ); |
| + | if ( setenv ( key, value, 1 )) |
| + | error_msg_and_die ( "out of memory" ); |
| } | |
| - | /* Become the user and group(s) specified by PW. */ |
| - | |
| - | void change_identity ( const struct passwd *pw ) |
| - | { |
| - | if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 ) |
| - | perror_msg_and_die ( "cannot set groups" ); |
| - | endgrent ( ); |
| - | |
| - | if ( setgid ( pw-> pw_gid )) |
| - | perror_msg_and_die ( "cannot set group id" ); |
| - | if ( setuid ( pw->pw_uid )) |
| - | perror_msg_and_die ( "cannot set user id" ); |
| - | } |
| - | |
| - | /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. |
| - | If COMMAND is nonzero, pass it to the shell with the -c option. |
| - | If ADDITIONAL_ARGS is nonzero, pass it to the shell as more |
| - | arguments. */ |
| - | |
| - | void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args ) |
| - | { |
| - | const char **args; |
| - | int argno = 1; |
| - | int additional_args_cnt = 0; |
| - | |
| - | for ( args = additional_args; args && *args; args++ ) |
| - | additional_args_cnt++; |
| - | |
| - | if ( additional_args ) |
| - | args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt )); |
| - | else |
| - | args = (const char **) xmalloc (sizeof (char *) * 4 ); |
| - | |
| - | args [0] = get_last_path_component ( xstrdup ( shell )); |
| - | |
| - | if ( loginshell ) { |
| - | char *args0 = xmalloc ( xstrlen ( args [0] ) + 2 ); |
| - | args0 [0] = '-'; |
| - | strcpy ( args0 + 1, args [0] ); |
| - | args [0] = args0; |
| - | } |
| - | |
| - | if ( command ) { |
| - | args [argno++] = "-c"; |
| - | args [argno++] = command; |
| - | } |
| - | if ( additional_args ) { |
| - | for ( ; *additional_args; ++additional_args ) |
| - | args [argno++] = *additional_args; |
| - | } |
| - | args [argno] = 0; |
| - | execv ( shell, (char **) args ); |
| - | perror_msg_and_die ( "cannot run %s", shell ); |
| - | } |
| - | |
| - | /* Return 1 if SHELL is a restricted shell (one not returned by |
| - | getusershell), else 0, meaning it is a standard shell. */ |
| - | |
| - | int restricted_shell ( const char *shell ) |
| - | { |
| - | char *line; |
| - | |
| - | setusershell ( ); |
| - | while (( line = getusershell ( ))) { |
| - | if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) |
| - | break; |
| - | } |
| - | endusershell ( ); |
| - | return line ? 0 : 1; |
| - | } |
| - | |
| - | /* Update `environ' for the new shell based on PW, with SHELL being |
| - | the value for the SHELL environment variable. */ |
| - | |
| void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ) | |
| { | |
| if ( loginshell ) { | |
| @@ -172,38 +91,3 @@ void setup_environment ( const char *shell, int loginshell, int changeenv, const | |
| } | |
| } | |
| - | /* Ask the user for a password. |
| - | Return 1 if the user gives the correct password for entry PW, |
| - | 0 if not. Return 1 without asking for a password if run by UID 0 |
| - | or if PW has an empty password. */ |
| - | |
| - | int correct_password ( const struct passwd *pw ) |
| - | { |
| - | char *unencrypted, *encrypted, *correct; |
| - | |
| - | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| - | if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) { |
| - | struct spwd *sp = getspnam ( pw-> pw_name ); |
| - | |
| - | if ( !sp ) |
| - | error_msg_and_die ( "no valid shadow password" ); |
| - | |
| - | correct = sp-> sp_pwdp; |
| - | } |
| - | else |
| - | #endif |
| - | correct = pw-> pw_passwd; |
| - | |
| - | if ( correct == 0 || correct[0] == '\0' ) |
| - | return 1; |
| - | |
| - | unencrypted = getpass ( "Password: " ); |
| - | if ( !unencrypted ) |
| - | { |
| - | fputs ( "getpass: cannot open /dev/tty\n", stderr ); |
| - | return 0; |
| - | } |
| - | encrypted = crypt ( unencrypted, correct ); |
| - | memset ( unencrypted, 0, xstrlen ( unencrypted )); |
| - | return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0; |
| - | } |
| @@ -26,9 +26,12 @@ LOGINUTILS-y:= | |
| LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o | |
| LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o | |
| LOGINUTILS-$(CONFIG_DELUSER) += deluser.o | |
| - | LOGINUTILS-$(CONFIG_GETTY) += getty.o |
| - | LOGINUTILS-$(CONFIG_LOGIN) += login.o tinylogin.o |
| - | LOGINUTILS-$(CONFIG_SU) += su.o tinylogin.o |
| + | LOGINUTILS-$(CONFIG_GETTY) += getty.o |
| + | LOGINUTILS-$(CONFIG_LOGIN) += login.o |
| + | LOGINUTILS-$(CONFIG_PASSWD) += passwd.o |
| + | LOGINUTILS-$(CONFIG_SU) += su.o |
| + | LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o |
| + | LOGINUTILS-$(CONFIG_VLOCK) += vlock.o |
| libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) | |
| @@ -35,9 +35,6 @@ | |
| #include "pwd.h" | |
| #include "grp.h" | |
| - | #define GROUP_FILE "/etc/group" |
| - | #define SHADOW_FILE "/etc/gshadow" |
| - | |
| /* structs __________________________ */ | |
| @@ -92,7 +89,7 @@ static int addgroup(const char *filename, char *group, gid_t gid) | |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| FILE *etc_gshadow; | |
| - | char *gshadow = SHADOW_FILE; |
| + | char *gshadow = shadow_file; |
| #endif | |
| struct group gr; | |
| @@ -162,7 +159,7 @@ int addgroup_main(int argc, char **argv) | |
| } | |
| /* werk */ | |
| - | return addgroup(GROUP_FILE, group, gid); |
| + | return addgroup(group_file, group, gid); |
| } | |
| - | /* $Id: addgroup.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ |
| + | /* $Id: addgroup.c,v 1.2 2002/06/23 04:24:24 andersen Exp $ */ |
| @@ -33,11 +33,7 @@ | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include "busybox.h" | |
| - | #include "pwd.h" |
| - | #include "grp.h" |
| - | #define PASSWD_FILE "/etc/passwd" |
| - | #define SHADOW_FILE "/etc/shadow" |
| /* structs __________________________ */ | |
| @@ -56,9 +52,6 @@ static const char default_home_prefix[] = "/home"; | |
| static const char default_shell[] = "/bin/sh"; | |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| - | |
| - | #include "shadow.h" |
| - | |
| /* shadow in use? */ | |
| static int shadow_enabled = 0; | |
| #endif | |
| @@ -138,47 +131,6 @@ static void passwd_wrapper(const char *login) | |
| error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually", login); | |
| } | |
| - | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| - | /* |
| - | * pwd_to_spwd - create entries for new spwd structure |
| - | * |
| - | * pwd_to_spwd() creates a new (struct spwd) containing the |
| - | * information in the pointed-to (struct passwd). |
| - | */ |
| - | #define DAY (24L*3600L) |
| - | #define WEEK (7*DAY) |
| - | #define SCALE DAY |
| - | static struct spwd *pwd_to_spwd(const struct passwd *pw) |
| - | { |
| - | static struct spwd sp; |
| - | |
| - | /* |
| - | * Nice, easy parts first. The name and passwd map directly |
| - | * from the old password structure to the new one. |
| - | */ |
| - | sp.sp_namp = pw->pw_name; |
| - | sp.sp_pwdp = pw->pw_passwd; |
| - | |
| - | /* |
| - | * Defaults used if there is no pw_age information. |
| - | */ |
| - | sp.sp_min = 0; |
| - | sp.sp_max = (10000L * DAY) / SCALE; |
| - | sp.sp_lstchg = time((time_t *) 0) / SCALE; |
| - | |
| - | /* |
| - | * These fields have no corresponding information in the password |
| - | * file. They are set to uninitialized values. |
| - | */ |
| - | sp.sp_warn = -1; |
| - | sp.sp_expire = -1; |
| - | sp.sp_inact = -1; |
| - | sp.sp_flag = -1; |
| - | |
| - | return &sp; |
| - | } |
| - | #endif |
| - | |
| /* putpwent(3) remix */ | |
| static int adduser(const char *filename, struct passwd *p) | |
| { | |
| @@ -222,7 +174,7 @@ static int adduser(const char *filename, struct passwd *p) | |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| /* add to shadow if necessary */ | |
| if (shadow_enabled) { | |
| - | shadow = wfopen(SHADOW_FILE, "a"); |
| + | shadow = wfopen(shadow_file, "a"); |
| if (shadow == NULL) { | |
| /* return -1; */ | |
| return 1; | |
| @@ -333,7 +285,7 @@ int adduser_main(int argc, char **argv) | |
| } | |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| /* is /etc/shadow in use? */ | |
| - | shadow_enabled = (0 == access(SHADOW_FILE, F_OK)); |
| + | shadow_enabled = (0 == access(shadow_file, F_OK)); |
| #endif | |
| /* create a passwd struct */ | |
| @@ -346,7 +298,7 @@ int adduser_main(int argc, char **argv) | |
| pw.pw_shell = (char *)shell; | |
| /* grand finale */ | |
| - | return adduser(PASSWD_FILE, &pw); |
| + | return adduser(passwd_file, &pw); |
| } | |
| - | /* $Id: adduser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ |
| + | /* $Id: adduser.c,v 1.2 2002/06/23 04:24:24 andersen Exp $ */ |
| @@ -7,19 +7,22 @@ mainmenu_option next_comment | |
| comment 'Login/Password Management Utilities' | |
| - | bool 'addgroup' CONFIG_ADDGROUP |
| - | bool 'adduser' CONFIG_ADDUSER |
| - | bool 'deluser' CONFIG_DELUSER |
| - | bool 'delgroup' CONFIG_DELUSER |
| - | bool 'getty' CONFIG_GETTY |
| + | bool 'addgroup' CONFIG_ADDGROUP |
| + | bool 'adduser' CONFIG_ADDUSER |
| + | bool 'deluser' CONFIG_DELUSER |
| + | bool 'delgroup' CONFIG_DELUSER |
| + | bool 'getty' CONFIG_GETTY |
| bool 'login' CONFIG_LOGIN | |
| if [ "$CONFIG_LOGIN" = "y" ]; then | |
| bool ' Support for /etc/securetty' CONFIG_FEATURE_SECURETTY | |
| fi | |
| - | bool 'su' CONFIG_SU |
| + | bool 'passwd' CONFIG_PASSWD |
| + | bool 'su' CONFIG_SU |
| if [ "$CONFIG_ADDUSER" = "y" -o "$CONFIG_DELUSER" = "y" -o "$CONFIG_LOGIN" = "y" -o "$CONFIG_SU" = "y" ]; then | |
| - | bool 'Support for shadow passwords' CONFIG_FEATURE_SHADOWPASSWDS |
| + | bool ' Support for shadow passwords' CONFIG_FEATURE_SHADOWPASSWDS |
| fi | |
| + | bool 'sulogin' CONFIG_SULOGIN |
| + | bool 'vlock' CONFIG_VLOCK |
| endmenu | |
| @@ -28,10 +28,6 @@ | |
| #include <string.h> | |
| #include "busybox.h" | |
| - | #define PASSWD_FILE "/etc/passwd" |
| - | #define GROUP_FILE "/etc/group" |
| - | #define SHADOW_FILE "/etc/shadow" |
| - | #define GSHADOW_FILE "/etc/gshadow" |
| /* where to start and stop deletion */ | |
| @@ -123,11 +119,11 @@ int delgroup_main(int argc, char **argv) | |
| show_usage(); | |
| } else { | |
| - | failure = del_line_matching(argv[1], GROUP_FILE); |
| + | failure = del_line_matching(argv[1], group_file); |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| - | if (access(GSHADOW_FILE, W_OK) == 0) { |
| + | if (access(gshadow_file, W_OK) == 0) { |
| /* EDR the |= works if the error is not 0, so he had it wrong */ | |
| - | failure |= del_line_matching(argv[1], GSHADOW_FILE); |
| + | failure |= del_line_matching(argv[1], gshadow_file); |
| } | |
| #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ | |
| /* if (!successful) { */ | |
| @@ -149,35 +145,35 @@ int deluser_main(int argc, char **argv) | |
| show_usage(); | |
| } else { | |
| - | failure = del_line_matching(argv[1], PASSWD_FILE); |
| + | failure = del_line_matching(argv[1], passwd_file); |
| /* if (!successful) { */ | |
| if (failure) { | |
| error_msg_and_die("%s: User could not be removed from %s\n", | |
| - | argv[1], PASSWD_FILE); |
| + | argv[1], passwd_file); |
| } | |
| #ifdef CONFIG_FEATURE_SHADOWPASSWDS | |
| - | failure = del_line_matching(argv[1], SHADOW_FILE); |
| + | failure = del_line_matching(argv[1], shadow_file); |
| /* if (!successful) { */ | |
| if (failure) { | |
| error_msg_and_die("%s: User could not be removed from %s\n", | |
| - | argv[1], SHADOW_FILE); |
| + | argv[1], shadow_file); |
| } | |
| - | failure = del_line_matching(argv[1], GSHADOW_FILE); |
| + | failure = del_line_matching(argv[1], gshadow_file); |
| /* if (!successful) { */ | |
| if (failure) { | |
| error_msg_and_die("%s: User could not be removed from %s\n", | |
| - | argv[1], GSHADOW_FILE); |
| + | argv[1], gshadow_file); |
| } | |
| #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ | |
| - | failure = del_line_matching(argv[1], GROUP_FILE); |
| + | failure = del_line_matching(argv[1], group_file); |
| /* if (!successful) { */ | |
| if (failure) { | |
| error_msg_and_die("%s: User could not be removed from %s\n", | |
| - | argv[1], GROUP_FILE); |
| + | argv[1], group_file); |
| } | |
| } | |
| return (EXIT_SUCCESS); | |
| } | |
| - | /* $Id: deluser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ |
| + | /* $Id: deluser.c,v 1.2 2002/06/23 04:24:24 andersen Exp $ */ |
| @@ -14,16 +14,9 @@ | |
| #include <sys/types.h> | |
| #include <ctype.h> | |
| #include <time.h> | |
| - | #include "busybox.h" |
| - | |
| - | #include "pwd.h" |
| - | #include "grp.h" |
| - | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| - | #include "shadow.h" |
| - | #endif |
| + | #include "busybox.h" |
| - | #include "tinylogin.h" |
| // import from utmp.c | |
| static void checkutmp(int picky); | |
| @@ -35,12 +28,7 @@ extern char *pw_encrypt(const char *clear, const char *salt); | |
| // login defines | |
| #define TIMEOUT 60 | |
| - | #define FAIL_DELAY 3 |
| #define EMPTY_USERNAME_COUNT 10 | |
| - | #define MOTD_FILE "/etc/motd" |
| - | #define NOLOGIN_FILE "/etc/nologin" |
| - | #define SECURETTY_FILE "/etc/securetty" |
| - | |
| #define USERNAME_SIZE 32 | |
| /* Stuff global to this file */ | |
| @@ -81,7 +69,9 @@ extern int login_main(int argc, char **argv) | |
| int failed; | |
| int count=0; | |
| struct passwd *pw, pw_copy; | |
| - | |
| + | #ifdef CONFIG_WHEEL_GROUP |
| + | struct group *grp; |
| + | #endif |
| int opt_preserve = 0; | |
| int opt_fflag = 0; | |
| char *opt_host = 0; | |
| @@ -283,11 +273,11 @@ static int login_prompt ( char *buf_name ) | |
| static int check_nologin ( int amroot ) | |
| { | |
| - | if ( access ( NOLOGIN_FILE, F_OK ) == 0 ) { |
| + | if ( access ( nologin_file, F_OK ) == 0 ) { |
| FILE *fp; | |
| int c; | |
| - | if (( fp = fopen ( NOLOGIN_FILE, "r" ))) { |
| + | if (( fp = fopen ( nologin_file, "r" ))) { |
| while (( c = getc ( fp )) != EOF ) | |
| putchar (( c == '\n' ) ? '\r' : c ); | |
| @@ -312,7 +302,7 @@ static int check_tty ( const char *tty ) | |
| int i; | |
| char buf[BUFSIZ]; | |
| - | if (( fp = fopen ( SECURETTY_FILE, "r" ))) { |
| + | if (( fp = fopen ( securetty_file, "r" ))) { |
| while ( fgets ( buf, sizeof( buf ) - 1, fp )) { | |
| for ( i = xstrlen( buf ) - 1; i >= 0; --i ) { | |
| if ( !isspace ( buf[i] )) | |
| @@ -358,7 +348,7 @@ static void motd ( ) | |
| FILE *fp; | |
| register int c; | |
| - | if (( fp = fopen ( MOTD_FILE, "r" ))) { |
| + | if (( fp = fopen ( motd_file, "r" ))) { |
| while (( c = getc ( fp )) != EOF ) | |
| putchar ( c ); | |
| fclose ( fp ); | |
| @@ -429,23 +419,6 @@ static void checkutmp(int picky) | |
| } | |
| } | |
| - | #if __GNU_LIBRARY__ < 5 |
| - | /* |
| - | * Some systems already have updwtmp() and possibly updwtmpx(). Others |
| - | * don't, so we re-implement these functions if necessary. --marekm |
| - | */ |
| - | static void updwtmp(const char *filename, const struct utmp *ut) |
| - | { |
| - | int fd; |
| - | |
| - | fd = open(filename, O_APPEND | O_WRONLY, 0); |
| - | if (fd >= 0) { |
| - | write(fd, (const char *) ut, sizeof(*ut)); |
| - | close(fd); |
| - | } |
| - | } |
| - | #endif |
| - | |
| /* | |
| * setutmp - put a USER_PROCESS entry in the utmp file | |
| * | |
| @@ -0,0 +1,408 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | #include <fcntl.h> |
| + | #include <stdio.h> |
| + | #include <string.h> |
| + | #include <signal.h> |
| + | #include <sys/stat.h> |
| + | #include <sys/types.h> |
| + | #include <unistd.h> |
| + | #include <utime.h> |
| + | #include <syslog.h> |
| + | #include <time.h> |
| + | #include <sys/resource.h> |
| + | #include <errno.h> |
| + | |
| + | #include "busybox.h" |
| + | |
| + | static char crypt_passwd[128]; |
| + | |
| + | static int create_backup(const char *backup, FILE * fp); |
| + | static int new_password(const struct passwd *pw, int amroot, int algo); |
| + | static void set_filesize_limit(int blocks); |
| + | |
| + | |
| + | int get_algo(char *a) |
| + | { |
| + | int x = 0; /* standart: DES */ |
| + | |
| + | if (strcasecmp(a, "md5") == 0) |
| + | x = 1; |
| + | return x; |
| + | } |
| + | |
| + | |
| + | extern int update_passwd(const struct passwd *pw, char *crypt_pw) |
| + | { |
| + | char filename[1024]; |
| + | char buf[1025]; |
| + | char buffer[80]; |
| + | char username[32]; |
| + | char *pw_rest; |
| + | int has_shadow = 0; |
| + | int mask; |
| + | int continued; |
| + | FILE *fp; |
| + | FILE *out_fp; |
| + | struct stat sb; |
| + | struct flock lock; |
| + | |
| + | if (access(shadow_file, F_OK) == 0) { |
| + | has_shadow = 1; |
| + | } |
| + | if (has_shadow) { |
| + | snprintf(filename, sizeof filename, "%s", shadow_file); |
| + | } else { |
| + | snprintf(filename, sizeof filename, "%s", passwd_file); |
| + | } |
| + | |
| + | if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) { |
| + | /* return 0; */ |
| + | return 1; |
| + | } |
| + | |
| + | /* Lock the password file before updating */ |
| + | lock.l_type = F_WRLCK; |
| + | lock.l_whence = SEEK_SET; |
| + | lock.l_start = 0; |
| + | lock.l_len = 0; |
| + | if (fcntl(fileno(fp), F_SETLK, &lock) < 0) { |
| + | fprintf(stderr, "%s: %s\n", filename, strerror(errno)); |
| + | return 1; |
| + | } |
| + | lock.l_type = F_UNLCK; |
| + | |
| + | snprintf(buf, sizeof buf, "%s-", filename); |
| + | if (create_backup(buf, fp)) { |
| + | fcntl(fileno(fp), F_SETLK, &lock); |
| + | fclose(fp); |
| + | return 1; |
| + | } |
| + | snprintf(buf, sizeof buf, "%s+", filename); |
| + | mask = umask(0777); |
| + | out_fp = fopen(buf, "w"); |
| + | umask(mask); |
| + | if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777)) |
| + | || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) { |
| + | fcntl(fileno(fp), F_SETLK, &lock); |
| + | fclose(fp); |
| + | fclose(out_fp); |
| + | return 1; |
| + | } |
| + | |
| + | continued = 0; |
| + | snprintf(username, sizeof username, "%s:", pw->pw_name); |
| + | rewind(fp); |
| + | while (!feof(fp)) { |
| + | fgets(buffer, sizeof buffer, fp); |
| + | if (!continued) { // Check to see if we're updating this line. |
| + | if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match. |
| + | pw_rest = strchr(buffer, ':'); |
| + | *pw_rest++ = '\0'; |
| + | pw_rest = strchr(pw_rest, ':'); |
| + | fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest); |
| + | } else { |
| + | fputs(buffer, out_fp); |
| + | } |
| + | } else { |
| + | fputs(buffer, out_fp); |
| + | } |
| + | if (buffer[strlen(buffer) - 1] == '\n') { |
| + | continued = 0; |
| + | } else { |
| + | continued = 1; |
| + | } |
| + | bzero(buffer, sizeof buffer); |
| + | } |
| + | |
| + | if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) { |
| + | unlink(buf); |
| + | fcntl(fileno(fp), F_SETLK, &lock); |
| + | fclose(fp); |
| + | return 1; |
| + | } |
| + | if (rename(buf, filename) < 0) { |
| + | fcntl(fileno(fp), F_SETLK, &lock); |
| + | fclose(fp); |
| + | return 1; |
| + | } else { |
| + | fcntl(fileno(fp), F_SETLK, &lock); |
| + | fclose(fp); |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | |
| + | extern int passwd_main(int argc, char **argv) |
| + | { |
| + | int amroot; |
| + | char *cp; |
| + | char *np; |
| + | char *name; |
| + | char *myname; |
| + | int flag; |
| + | int algo = 0; /* -a - password algorithm */ |
| + | int lflg = 0; /* -l - lock account */ |
| + | int uflg = 0; /* -u - unlock account */ |
| + | int dflg = 0; /* -d - delete password */ |
| + | const struct passwd *pw; |
| + | unsigned short ruid; |
| + | |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | const struct spwd *sp; |
| + | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| + | amroot = (getuid() == 0); |
| + | openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); |
| + | while ((flag = getopt(argc, argv, "a:dlu")) != EOF) { |
| + | switch (flag) { |
| + | case 'a': |
| + | algo = get_algo(optarg); |
| + | break; |
| + | case 'd': |
| + | dflg++; |
| + | break; |
| + | case 'l': |
| + | lflg++; |
| + | break; |
| + | case 'u': |
| + | uflg++; |
| + | break; |
| + | default: |
| + | show_usage(); |
| + | } |
| + | } |
| + | ruid = getuid(); |
| + | pw = (struct passwd *) getpwuid(ruid); |
| + | if (!pw) { |
| + | error_msg_and_die("Cannot determine your user name.\n"); |
| + | } |
| + | myname = (char *) xstrdup(pw->pw_name); |
| + | if (optind < argc) { |
| + | name = argv[optind]; |
| + | } else { |
| + | name = myname; |
| + | } |
| + | if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) { |
| + | show_usage(); |
| + | } |
| + | pw = getpwnam(name); |
| + | if (!pw) { |
| + | error_msg_and_die("Unknown user %s\n", name); |
| + | } |
| + | if (!amroot && pw->pw_uid != getuid()) { |
| + | syslog(LOG_WARNING, "can't change pwd for `%s'", name); |
| + | error_msg_and_die("Permission denied.\n"); |
| + | } |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | sp = getspnam(name); |
| + | if (!sp) { |
| + | sp = (struct spwd *) pwd_to_spwd(pw); |
| + | } |
| + | cp = sp->sp_pwdp; |
| + | np = sp->sp_namp; |
| + | #else |
| + | cp = pw->pw_passwd; |
| + | np = name; |
| + | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| + | |
| + | safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); |
| + | if (!(dflg || lflg || uflg)) { |
| + | if (!amroot) { |
| + | if (cp[0] == '!') { |
| + | syslog(LOG_WARNING, "password locked for `%s'", np); |
| + | error_msg_and_die( "The password for `%s' cannot be changed.\n", np); |
| + | } |
| + | } |
| + | printf("Changing password for %s\n", name); |
| + | if (new_password(pw, amroot, algo)) { |
| + | error_msg_and_die( "The password for %s is unchanged.\n", name); |
| + | } |
| + | } else if (lflg) { |
| + | if (crypt_passwd[0] != '!') { |
| + | memmove(&crypt_passwd[1], crypt_passwd, |
| + | sizeof crypt_passwd - 1); |
| + | crypt_passwd[sizeof crypt_passwd - 1] = '\0'; |
| + | crypt_passwd[0] = '!'; |
| + | } |
| + | } else if (uflg) { |
| + | if (crypt_passwd[0] == '!') { |
| + | memmove(crypt_passwd, &crypt_passwd[1], |
| + | sizeof crypt_passwd - 1); |
| + | } |
| + | } else if (dflg) { |
| + | crypt_passwd[0] = '\0'; |
| + | } |
| + | set_filesize_limit(30000); |
| + | signal(SIGHUP, SIG_IGN); |
| + | signal(SIGINT, SIG_IGN); |
| + | signal(SIGQUIT, SIG_IGN); |
| + | umask(077); |
| + | if (setuid(0)) { |
| + | syslog(LOG_ERR, "can't setuid(0)"); |
| + | error_msg_and_die( "Cannot change ID to root.\n"); |
| + | } |
| + | if (!update_passwd(pw, crypt_passwd)) { |
| + | syslog(LOG_INFO, "password for `%s' changed by user `%s'", name, |
| + | myname); |
| + | printf("Password changed.\n"); |
| + | } else { |
| + | syslog(LOG_WARNING, |
| + | "an error occurred updating the password file"); |
| + | error_msg_and_die("An error occurred updating the password file.\n"); |
| + | } |
| + | return (0); |
| + | } |
| + | |
| + | |
| + | |
| + | static int create_backup(const char *backup, FILE * fp) |
| + | { |
| + | struct stat sb; |
| + | struct utimbuf ub; |
| + | FILE *bkfp; |
| + | int c, mask; |
| + | |
| + | if (fstat(fileno(fp), &sb)) |
| + | /* return -1; */ |
| + | return 1; |
| + | |
| + | mask = umask(077); |
| + | bkfp = fopen(backup, "w"); |
| + | umask(mask); |
| + | if (!bkfp) |
| + | /* return -1; */ |
| + | return 1; |
| + | |
| + | /* TODO: faster copy, not one-char-at-a-time. --marekm */ |
| + | rewind(fp); |
| + | while ((c = getc(fp)) != EOF) { |
| + | if (putc(c, bkfp) == EOF) |
| + | break; |
| + | } |
| + | if (c != EOF || fflush(bkfp)) { |
| + | fclose(bkfp); |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | if (fclose(bkfp)) |
| + | /* return -1; */ |
| + | return 1; |
| + | |
| + | ub.actime = sb.st_atime; |
| + | ub.modtime = sb.st_mtime; |
| + | utime(backup, &ub); |
| + | return 0; |
| + | } |
| + | |
| + | static int i64c(int i) |
| + | { |
| + | if (i <= 0) |
| + | return ('.'); |
| + | if (i == 1) |
| + | return ('/'); |
| + | if (i >= 2 && i < 12) |
| + | return ('0' - 2 + i); |
| + | if (i >= 12 && i < 38) |
| + | return ('A' - 12 + i); |
| + | if (i >= 38 && i < 63) |
| + | return ('a' - 38 + i); |
| + | return ('z'); |
| + | } |
| + | |
| + | static char *crypt_make_salt(void) |
| + | { |
| + | time_t now; |
| + | static unsigned long x; |
| + | static char result[3]; |
| + | |
| + | time(&now); |
| + | x += now + getpid() + clock(); |
| + | result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077); |
| + | result[1] = i64c(((x >> 12) ^ x) & 077); |
| + | result[2] = '\0'; |
| + | return result; |
| + | } |
| + | |
| + | |
| + | static int new_password(const struct passwd *pw, int amroot, int algo) |
| + | { |
| + | char *clear; |
| + | char *cipher; |
| + | char *cp; |
| + | char orig[200]; |
| + | char pass[200]; |
| + | time_t start, now; |
| + | |
| + | if (!amroot && crypt_passwd[0]) { |
| + | if (!(clear = getpass("Old password:"))) { |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | cipher = pw_encrypt(clear, crypt_passwd); |
| + | if (strcmp(cipher, crypt_passwd) != 0) { |
| + | syslog(LOG_WARNING, "incorrect password for `%s'", |
| + | pw->pw_name); |
| + | time(&start); |
| + | now = start; |
| + | while (difftime(now, start) < FAIL_DELAY) { |
| + | sleep(FAIL_DELAY); |
| + | time(&now); |
| + | } |
| + | fprintf(stderr, "Incorrect password.\n"); |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | safe_strncpy(orig, clear, sizeof(orig)); |
| + | bzero(clear, strlen(clear)); |
| + | bzero(cipher, strlen(cipher)); |
| + | } else { |
| + | orig[0] = '\0'; |
| + | } |
| + | if (! |
| + | (cp = |
| + | getpass ("Enter the new password (minimum of 5, maximum of 8 characters)\n"" |
| + | Please use a combination of upper and lower case letters and numbers.\nEnter new password: "))) |
| + | { |
| + | bzero(orig, sizeof orig); |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | safe_strncpy(pass, cp, sizeof(pass)); |
| + | bzero(cp, strlen(cp)); |
| + | /* if (!obscure(orig, pass, pw)) { */ |
| + | if (obscure(orig, pass, pw)) { |
| + | if (amroot) { |
| + | printf("\nWarning: weak password (continuing).\n"); |
| + | } else { |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | } |
| + | if (!(cp = getpass("Re-enter new password: "))) { |
| + | bzero(orig, sizeof orig); |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | if (strcmp(cp, pass)) { |
| + | fprintf(stderr, "Passwords do not match.\n"); |
| + | /* return -1; */ |
| + | return 1; |
| + | } |
| + | bzero(cp, strlen(cp)); |
| + | bzero(orig, sizeof(orig)); |
| + | |
| + | if (algo == 1) { |
| + | cp = pw_encrypt(pass, "$1$"); |
| + | } else |
| + | cp = pw_encrypt(pass, crypt_make_salt()); |
| + | bzero(pass, sizeof pass); |
| + | safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); |
| + | return 0; |
| + | } |
| + | |
| + | static void set_filesize_limit(int blocks) |
| + | { |
| + | struct rlimit rlimit_fsize; |
| + | |
| + | rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks; |
| + | setrlimit(RLIMIT_FSIZE, &rlimit_fsize); |
| + | } |
| @@ -1,7 +1,5 @@ | |
| /* vi: set sw=4 ts=4: */ | |
| - | #include "busybox.h" |
| - | |
| #include <fcntl.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| @@ -18,10 +16,7 @@ | |
| #include <ctype.h> | |
| #include <time.h> | |
| - | #include "pwd.h" |
| - | #include "grp.h" |
| - | |
| - | #include "tinylogin.h" |
| + | #include "busybox.h" |
| @@ -161,7 +156,7 @@ int su_main ( int argc, char **argv ) | |
| change_identity ( pw ); | |
| setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw ); | |
| - | run_shell ( opt_shell, opt_loginshell, opt_command, opt_args ); |
| + | run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args ); |
| return EXIT_FAILURE; | |
| } | |
| @@ -0,0 +1,184 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | #include <fcntl.h> |
| + | #include <signal.h> |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <string.h> |
| + | #include <syslog.h> |
| + | #include <termios.h> |
| + | #include <unistd.h> |
| + | #include <utmp.h> |
| + | #include <sys/resource.h> |
| + | #include <sys/stat.h> |
| + | #include <sys/time.h> |
| + | #include <sys/types.h> |
| + | #include <ctype.h> |
| + | #include <time.h> |
| + | |
| + | #include "busybox.h" |
| + | |
| + | |
| + | // sulogin defines |
| + | #define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \ |
| + | "(or type Control-D for normal startup):" |
| + | |
| + | static const char *forbid[] = { |
| + | "ENV", |
| + | "BASH_ENV", |
| + | "HOME", |
| + | "IFS", |
| + | "PATH", |
| + | "SHELL", |
| + | "LD_LIBRARY_PATH", |
| + | "LD_PRELOAD", |
| + | "LD_TRACE_LOADED_OBJECTS", |
| + | "LD_BIND_NOW", |
| + | "LD_AOUT_LIBRARY_PATH", |
| + | "LD_AOUT_PRELOAD", |
| + | "LD_NOWARN", |
| + | "LD_KEEPDIR", |
| + | (char *) 0 |
| + | }; |
| + | |
| + | |
| + | |
| + | static void catchalarm(int junk) |
| + | { |
| + | exit(EXIT_FAILURE); |
| + | } |
| + | |
| + | |
| + | extern int sulogin_main(int argc, char **argv) |
| + | { |
| + | char *cp; |
| + | char *device = (char *) 0; |
| + | const char *name = "root"; |
| + | int timeout = 0; |
| + | static char pass[BUFSIZ]; |
| + | struct termios termio; |
| + | struct passwd pwent; |
| + | struct passwd *pwd; |
| + | time_t start, now; |
| + | const char **p; |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | struct spwd *spwd = NULL; |
| + | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| + | |
| + | tcgetattr(0, &termio); |
| + | /* set control chars */ |
| + | termio.c_cc[VINTR] = 3; /* C-c */ |
| + | termio.c_cc[VQUIT] = 28; /* C-\ */ |
| + | termio.c_cc[VERASE] = 127; /* C-? */ |
| + | termio.c_cc[VKILL] = 21; /* C-u */ |
| + | termio.c_cc[VEOF] = 4; /* C-d */ |
| + | termio.c_cc[VSTART] = 17; /* C-q */ |
| + | termio.c_cc[VSTOP] = 19; /* C-s */ |
| + | termio.c_cc[VSUSP] = 26; /* C-z */ |
| + | /* use line dicipline 0 */ |
| + | termio.c_line = 0; |
| + | /* Make it be sane */ |
| + | termio.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD; |
| + | termio.c_cflag |= CREAD|HUPCL|CLOCAL; |
| + | /* input modes */ |
| + | termio.c_iflag = ICRNL | IXON | IXOFF; |
| + | /* output modes */ |
| + | termio.c_oflag = OPOST | ONLCR; |
| + | /* local modes */ |
| + | termio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; |
| + | tcsetattr(0, TCSANOW, &termio); |
| + | openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); |
| + | if (argc > 1) { |
| + | if (strncmp(argv[1], "-t", 2) == 0) { |
| + | if (strcmp(argv[1], "-t") == 0) { |
| + | if (argc > 2) { |
| + | timeout = atoi(argv[2]); |
| + | if (argc > 3) { |
| + | device = argv[3]; |
| + | } |
| + | } |
| + | } else { |
| + | if (argc > 2) { |
| + | device = argv[2]; |
| + | } |
| + | } |
| + | } else { |
| + | device = argv[1]; |
| + | } |
| + | if (device) { |
| + | close(0); |
| + | close(1); |
| + | close(2); |
| + | if (open(device, O_RDWR) >= 0) { |
| + | dup(0); |
| + | dup(0); |
| + | } else { |
| + | syslog(LOG_WARNING, "cannot open %s\n", device); |
| + | exit(EXIT_FAILURE); |
| + | } |
| + | } |
| + | } |
| + | if (access(passwd_file, 0) == -1) { |
| + | syslog(LOG_WARNING, "No password file\n"); |
| + | error_msg_and_die("No password file\n"); |
| + | } |
| + | if (!isatty(0) || !isatty(1) || !isatty(2)) { |
| + | exit(EXIT_FAILURE); |
| + | } |
| + | |
| + | |
| + | /* Clear out anything dangerous from the environment */ |
| + | for (p = forbid; *p; p++) |
| + | unsetenv(*p); |
| + | |
| + | |
| + | signal(SIGALRM, catchalarm); |
| + | alarm(timeout); |
| + | if (!(pwd = getpwnam(name))) { |
| + | syslog(LOG_WARNING, "No password entry for `root'\n"); |
| + | error_msg_and_die("No password entry for `root'\n"); |
| + | } |
| + | pwent = *pwd; |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | spwd = NULL; |
| + | if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0) |
| + | || (strcmp(pwd->pw_passwd, "*") == 0))) { |
| + | endspent(); |
| + | spwd = getspnam(name); |
| + | if (spwd) { |
| + | pwent.pw_passwd = spwd->sp_pwdp; |
| + | } |
| + | } |
| + | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| + | while (1) { |
| + | cp = getpass(SULOGIN_PROMPT); |
| + | if (!cp || !*cp) { |
| + | puts("\n"); |
| + | fflush(stdout); |
| + | syslog(LOG_INFO, "Normal startup\n"); |
| + | exit(EXIT_SUCCESS); |
| + | } else { |
| + | safe_strncpy(pass, cp, sizeof(pass)); |
| + | bzero(cp, strlen(cp)); |
| + | } |
| + | if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) { |
| + | break; |
| + | } |
| + | time(&start); |
| + | now = start; |
| + | while (difftime(now, start) < FAIL_DELAY) { |
| + | sleep(FAIL_DELAY); |
| + | time(&now); |
| + | } |
| + | puts("Login incorrect"); |
| + | fflush(stdout); |
| + | syslog(LOG_WARNING, "Incorrect root password\n"); |
| + | } |
| + | bzero(pass, strlen(pass)); |
| + | alarm(0); |
| + | signal(SIGALRM, SIG_DFL); |
| + | puts("Entering System Maintenance Mode\n"); |
| + | fflush(stdout); |
| + | syslog(LOG_INFO, "System Maintenance Mode\n"); |
| + | run_shell(pwent.pw_shell, 1, 0, 0); |
| + | return (0); |
| + | } |
| @@ -1,10 +0,0 @@ | |
| - | #ifndef BB_LOGINUTILS_SHELL_H |
| - | #define BB_LOGINUTILS_SHELL_H |
| - | |
| - | extern void change_identity ( const struct passwd *pw ); |
| - | extern void run_shell ( const char *shell, int loginshell, const char *command, char **additional_args ); |
| - | extern int restricted_shell ( const char *shell ); |
| - | extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ); |
| - | extern int correct_password ( const struct passwd *pw ); |
| - | |
| - | #endif |
| @@ -0,0 +1,229 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * vlock implementation for busybox |
| + | * |
| + | * Copyright (C) 2000 by spoon <[email protected]> |
| + | * Written by spoon <[email protected]> |
| + | * |
| + | * This program is free software; you can redistribute it and/or modify |
| + | * it under the terms of the GNU General Public License as published by |
| + | * the Free Software Foundation; either version 2 of the License, or |
| + | * (at your option) any later version. |
| + | * |
| + | * This program is distributed in the hope that it will be useful, |
| + | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | * General Public License for more details. |
| + | * |
| + | * You should have received a copy of the GNU General Public License |
| + | * along with this program; if not, write to the Free Software |
| + | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + | * |
| + | */ |
| + | |
| + | /* Shoutz to Michael K. Johnson <[email protected]>, author of the |
| + | * original vlock. I snagged a bunch of his code to write this |
| + | * minimalistic vlock. |
| + | */ |
| + | /* Fixed by Erik Andersen to do passwords the tinylogin way... |
| + | * It now works with md5, sha1, etc passwords. */ |
| + | |
| + | #include <stdio.h> |
| + | #include <sys/vt.h> |
| + | #include <signal.h> |
| + | #include <string.h> |
| + | #include <unistd.h> |
| + | #include <fcntl.h> |
| + | #include <errno.h> |
| + | #include <sys/ioctl.h> |
| + | #include <termios.h> |
| + | |
| + | #include "busybox.h" |
| + | |
| + | static struct passwd *pw; |
| + | static struct spwd *spw; |
| + | static struct vt_mode ovtm; |
| + | static struct termios oterm; |
| + | static int vfd; |
| + | static int o_lock_all = 0; |
| + | |
| + | /* getspuid - get a shadow entry by uid */ |
| + | struct spwd *getspuid(uid_t uid) |
| + | { |
| + | struct spwd *sp; |
| + | struct passwd *mypw; |
| + | |
| + | if ((mypw = getpwuid(getuid())) == NULL) { |
| + | return (NULL); |
| + | } |
| + | setspent(); |
| + | while ((sp = getspent()) != NULL) { |
| + | if (strcmp(mypw->pw_name, sp->sp_namp) == 0) |
| + | break; |
| + | } |
| + | endspent(); |
| + | return (sp); |
| + | } |
| + | |
| + | static void release_vt(int signo) |
| + | { |
| + | if (!o_lock_all) |
| + | ioctl(vfd, VT_RELDISP, 1); |
| + | else |
| + | ioctl(vfd, VT_RELDISP, 0); |
| + | } |
| + | |
| + | static void acquire_vt(int signo) |
| + | { |
| + | ioctl(vfd, VT_RELDISP, VT_ACKACQ); |
| + | } |
| + | |
| + | static void restore_terminal(void) |
| + | { |
| + | ioctl(vfd, VT_SETMODE, &ovtm); |
| + | tcsetattr(STDIN_FILENO, TCSANOW, &oterm); |
| + | } |
| + | |
| + | extern int vlock_main(int argc, char **argv) |
| + | { |
| + | sigset_t sig; |
| + | struct sigaction sa; |
| + | struct vt_mode vtm; |
| + | int times = 0; |
| + | struct termios term; |
| + | |
| + | if (argc > 2) { |
| + | show_usage(); |
| + | } |
| + | |
| + | if (argc == 2) { |
| + | if (strncmp(argv[1], "-a", 2)) { |
| + | show_usage(); |
| + | } else { |
| + | o_lock_all = 1; |
| + | } |
| + | } |
| + | |
| + | if ((pw = getpwuid(getuid())) == NULL) { |
| + | error_msg_and_die("no password for uid %d\n", getuid()); |
| + | } |
| + | #ifdef CONFIG_FEATURE_SHADOWPASSWDS |
| + | if ((strcmp(pw->pw_passwd, "x") == 0) |
| + | || (strcmp(pw->pw_passwd, "*") == 0)) { |
| + | |
| + | if ((spw = getspuid(getuid())) == NULL) { |
| + | error_msg_and_die("could not read shadow password for uid %d: %s\n", |
| + | getuid(), strerror(errno)); |
| + | } |
| + | if (spw->sp_pwdp) { |
| + | pw->pw_passwd = spw->sp_pwdp; |
| + | } |
| + | } |
| + | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ |
| + | if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') { |
| + | error_msg_and_die("Account disabled for uid %d\n", getuid()); |
| + | } |
| + | |
| + | /* we no longer need root privs */ |
| + | setuid(getuid()); |
| + | setgid(getgid()); |
| + | |
| + | if ((vfd = open("/dev/tty", O_RDWR)) < 0) { |
| + | error_msg_and_die("/dev/tty"); |
| + | }; |
| + | |
| + | if (ioctl(vfd, VT_GETMODE, &vtm) < 0) { |
| + | error_msg_and_die("/dev/tty"); |
| + | }; |
| + | |
| + | /* mask a bunch of signals */ |
| + | sigprocmask(SIG_SETMASK, NULL, &sig); |
| + | sigdelset(&sig, SIGUSR1); |
| + | sigdelset(&sig, SIGUSR2); |
| + | sigaddset(&sig, SIGTSTP); |
| + | sigaddset(&sig, SIGTTIN); |
| + | sigaddset(&sig, SIGTTOU); |
| + | sigaddset(&sig, SIGHUP); |
| + | sigaddset(&sig, SIGCHLD); |
| + | sigaddset(&sig, SIGQUIT); |
| + | sigaddset(&sig, SIGINT); |
| + | |
| + | sigemptyset(&(sa.sa_mask)); |
| + | sa.sa_flags = SA_RESTART; |
| + | sa.sa_handler = release_vt; |
| + | sigaction(SIGUSR1, &sa, NULL); |
| + | sa.sa_handler = acquire_vt; |
| + | sigaction(SIGUSR2, &sa, NULL); |
| + | |
| + | /* need to handle some signals so that we don't get killed by them */ |
| + | sa.sa_handler = SIG_IGN; |
| + | sigaction(SIGHUP, &sa, NULL); |
| + | sigaction(SIGQUIT, &sa, NULL); |
| + | sigaction(SIGINT, &sa, NULL); |
| + | sigaction(SIGTSTP, &sa, NULL); |
| + | |
| + | ovtm = vtm; |
| + | vtm.mode = VT_PROCESS; |
| + | vtm.relsig = SIGUSR1; |
| + | vtm.acqsig = SIGUSR2; |
| + | ioctl(vfd, VT_SETMODE, &vtm); |
| + | |
| + | tcgetattr(STDIN_FILENO, &oterm); |
| + | term = oterm; |
| + | term.c_iflag &= ~BRKINT; |
| + | term.c_iflag |= IGNBRK; |
| + | term.c_lflag &= ~ISIG; |
| + | term.c_lflag &= ~(ECHO | ECHOCTL); |
| + | tcsetattr(STDIN_FILENO, TCSANOW, &term); |
| + | |
| + | do { |
| + | char *pass, *crypt_pass; |
| + | char prompt[100]; |
| + | |
| + | if (o_lock_all) { |
| + | printf("All Virtual Consoles locked.\n"); |
| + | } else { |
| + | printf("This Virtual Console locked.\n"); |
| + | } |
| + | fflush(stdout); |
| + | |
| + | snprintf(prompt, 100, "%s's password: ", pw->pw_name); |
| + | |
| + | if ((pass = getpass(prompt)) == NULL) { |
| + | perror("getpass"); |
| + | restore_terminal(); |
| + | exit(1); |
| + | } |
| + | |
| + | crypt_pass = pw_encrypt(pass, pw->pw_passwd); |
| + | if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) { |
| + | memset(pass, 0, strlen(pass)); |
| + | memset(crypt_pass, 0, strlen(crypt_pass)); |
| + | restore_terminal(); |
| + | return 0; |
| + | } |
| + | memset(pass, 0, strlen(pass)); |
| + | memset(crypt_pass, 0, strlen(crypt_pass)); |
| + | |
| + | if (isatty(STDIN_FILENO) == 0) { |
| + | perror("isatty"); |
| + | restore_terminal(); |
| + | exit(1); |
| + | } |
| + | |
| + | sleep(++times); |
| + | printf("Password incorrect.\n"); |
| + | if (times >= 3) { |
| + | sleep(15); |
| + | times = 2; |
| + | } |
| + | } while (1); |
| + | } |
| + | |
| + | /* |
| + | Local Variables: |
| + | c-file-style: "linux" |
| + | c-basic-offset: 4 |
| + | tab-width: 4 |
| + | End: |
| + | */ |