]> granicus.if.org Git - shadow/blob - src/sulogin.c
* src/chgpasswd.c: Add annotations to indicate that usage() does
[shadow] / src / sulogin.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2002 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2010, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <sys/ioctl.h>
42 #include "defines.h"
43 #include "getdef.h"
44 #include "prototypes.h"
45 #include "pwauth.h"
46 /*@-exitarg@*/
47 #include "exitcodes.h"
48
49 /*
50  * Global variables
51  */
52 const char *Prog;
53
54 static char name[BUFSIZ];
55 static char pass[BUFSIZ];
56
57 static struct passwd pwent;
58
59 extern char **newenvp;
60 extern size_t newenvc;
61
62 extern char **environ;
63
64 #ifndef ALARM
65 #define ALARM   60
66 #endif
67
68 /* local function prototypes */
69 static RETSIGTYPE catch_signals (int);
70
71 static RETSIGTYPE catch_signals (unused int sig)
72 {
73         exit (1);
74 }
75
76 /*
77  * syslogd is usually not running at the time when sulogin is typically
78  * called, cluttering the screen with unnecessary messages. Suggested by
79  * Ivan Nejgebauer <ian@unsux.ns.ac.yu>.  --marekm
80  */
81 #undef USE_SYSLOG
82
83  /*ARGSUSED*/ int main (int argc, char **argv)
84 {
85         const char *env;
86         char **envp = environ;
87         TERMIO termio;
88         int err = 0;
89
90 #ifdef  USE_TERMIO
91         ioctl (0, TCGETA, &termio);
92         termio.c_iflag |= (ICRNL | IXON);
93         termio.c_oflag |= (OPOST | ONLCR);
94         termio.c_cflag |= (CREAD);
95         termio.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK);
96         ioctl (0, TCSETAF, &termio);
97 #endif
98 #ifdef  USE_TERMIOS
99         tcgetattr (0, &termio);
100         termio.c_iflag |= (ICRNL | IXON);
101         termio.c_oflag |= (CREAD);
102         termio.c_lflag |= (ECHO | ECHOE | ECHOK | ICANON | ISIG);
103         tcsetattr (0, TCSANOW, &termio);
104 #endif
105
106         Prog = Basename (argv[0]);
107         (void) setlocale (LC_ALL, "");
108         (void) bindtextdomain (PACKAGE, LOCALEDIR);
109         (void) textdomain (PACKAGE);
110
111 #ifdef  USE_SYSLOG
112         OPENLOG ("sulogin");
113 #endif
114         initenv ();
115         if (argc > 1) {
116                 close (0);
117                 close (1);
118                 close (2);
119
120                 if (open (argv[1], O_RDWR) >= 0) {
121                         dup (0);
122                         dup (0);
123                 } else {
124 #ifdef  USE_SYSLOG
125                         SYSLOG (LOG_WARN, "cannot open %s\n", argv[1]);
126                         closelog ();
127 #endif
128                         exit (1);
129                 }
130         }
131         if (access (PASSWD_FILE, F_OK) == -1) { /* must be a password file! */
132                 (void) puts (_("No password file"));
133 #ifdef  USE_SYSLOG
134                 SYSLOG (LOG_WARN, "No password file\n");
135                 closelog ();
136 #endif
137                 exit (1);
138         }
139 #if !defined(DEBUG) && defined(SULOGIN_ONLY_INIT)
140         if (getppid () != 1) {  /* parent must be INIT */
141 #ifdef  USE_SYSLOG
142                 SYSLOG (LOG_WARN, "Pid == %d, not 1\n", getppid ());
143                 closelog ();
144 #endif
145                 exit (1);
146         }
147 #endif
148         if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) {
149 #ifdef  USE_SYSLOG
150                 closelog ();
151 #endif
152                 exit (1);       /* must be a terminal */
153         }
154         /* If we were init, we need to start a new session */
155         if (getppid() == 1) {
156                 setsid();
157                 if (ioctl(0, TIOCSCTTY, 1) != 0) {
158                         (void) fputs (_("TIOCSCTTY failed"), stderr);
159                 }
160         }
161         while (NULL != *envp) {         /* add inherited environment, */
162                 addenv (*envp, NULL);   /* some variables change later */
163                 envp++;
164         }
165
166 #ifndef USE_PAM
167
168         env = getdef_str ("ENV_TZ");
169         if (NULL != env) {
170                 addenv (('/' == *env) ? tz (env) : env, NULL);
171         }
172         env = getdef_str ("ENV_HZ");
173         if (NULL != env) {
174                 addenv (env, NULL);     /* set the default $HZ, if one */
175         }
176 #endif                          /* !USE_PAM */
177
178         (void) strcpy (name, "root");   /* KLUDGE!!! */
179
180         (void) signal (SIGALRM, catch_signals); /* exit if the timer expires */
181         (void) alarm (ALARM);           /* only wait so long ... */
182
183         while (true) {          /* repeatedly get login/password pairs */
184                 char *cp;
185                 pw_entry (name, &pwent);        /* get entry from password file */
186                 if (pwent.pw_name == (char *) 0) {
187                         /*
188                          * Fail secure
189                          */
190                         (void) puts (_("No password entry for 'root'"));
191 #ifdef  USE_SYSLOG
192                         SYSLOG (LOG_WARN, "No password entry for 'root'\n");
193                         closelog ();
194 #endif
195                         exit (1);
196                 }
197
198                 /*
199                  * Here we prompt for the root password, or if no password
200                  * is given we just exit.
201                  */
202
203                 /* get a password for root */
204                 cp = getpass (_(
205 "\n"
206 "Type control-d to proceed with normal startup,\n"
207 "(or give root password for system maintenance):"));
208                 /*
209                  * XXX - can't enter single user mode if root password is
210                  * empty.  I think this doesn't happen very often :-). But
211                  * it will work with standard getpass() (no NULL on EOF). 
212                  * --marekm
213                  */
214                 if ((NULL == cp) || ('\0' == *cp)) {
215 #ifdef  USE_SYSLOG
216                         SYSLOG (LOG_INFO, "Normal startup\n");
217                         closelog ();
218 #endif
219                         (void) puts ("");
220 #ifdef  TELINIT
221                         execl (PATH_TELINIT, "telinit", RUNLEVEL, (char *) 0);
222 #endif
223                         exit (0);
224                 } else {
225                         STRFCPY (pass, cp);
226                         strzero (cp);
227                 }
228                 if (valid (pass, &pwent)) {     /* check encrypted passwords ... */
229                         break;  /* ... encrypted passwords matched */
230                 }
231
232 #ifdef  USE_SYSLOG
233                 SYSLOG (LOG_WARN, "Incorrect root password\n");
234 #endif
235                 sleep (2);
236                 (void) puts (_("Login incorrect"));
237         }
238         strzero (pass);
239         (void) alarm (0);
240         (void) signal (SIGALRM, SIG_DFL);
241         environ = newenvp;      /* make new environment active */
242
243         (void) puts (_("Entering System Maintenance Mode"));
244 #ifdef  USE_SYSLOG
245         SYSLOG (LOG_INFO, "System Maintenance Mode\n");
246 #endif
247
248 #ifdef  USE_SYSLOG
249         closelog ();
250 #endif
251         /* exec the shell finally. */
252         err = shell (pwent.pw_shell, (char *) 0, environ);
253
254         return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
255 }
256