The Pedigree Project  0.1
login.c
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #define _GNU_SOURCE 1
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <libintl.h>
26 #include <locale.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/klog.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36 #include <termios.h>
37 #include <unistd.h>
38 #include <utmp.h>
39 #include <utmpx.h>
40 
41 // Force immediate login if we're running a live CD.
42 #ifdef LIVECD
43 #define FORCE_LOGIN_USER "root"
44 #define FORCE_LOGIN_PASS "root"
45 #endif
46 
47 // PID of the process we're running
48 int g_RunningPid = -1;
49 
50 // Pedigree function, from libpedigree-c
51 extern int pedigree_login(int uid, const char *password);
52 
53 // SIGINT handler
54 void sigint(int sig)
55 {
56  // If we're in the background...
57  if (g_RunningPid != -1)
58  {
59  // Ignore, but don't log (running program)
60  }
61  else
62  {
63  // Do not kill us! CTRL-C does not do anything while the login prompt
64  // is active
65  klog(LOG_NOTICE, "SIGINT ignored");
66  }
67 }
68 
69 int main(int argc, char **argv)
70 {
71  setlocale(LC_ALL, "");
72  bindtextdomain("login", "/system/locale");
73  bind_textdomain_codeset("login", "UTF-8");
74  textdomain("login");
75 
76 #ifdef INSTALLER
77  // For the installer, just run Python
78  printf("Loading installer, please wait...\n");
79 
80  static const char *app_argv[] = {"root»/applications/python",
81  "root»/code/installer/install.py", 0};
82  static const char *app_env[] = {"TERM=xterm", "PATH=/applications",
83  "PYTHONHOME=/", 0};
84  execve(
85  "root»/applications/python", (char *const *) app_argv,
86  (char *const *) app_env);
87 
88  printf("FATAL: Couldn't load Python!\n");
89 
90  return 0;
91 #endif
92 
93 // Are we on Travis-CI?
94 #ifdef TRAVIS
95  klog(LOG_INFO, "-- Hello, Travis! --");
96 #endif
97 
98  // New process group for job control. We'll ignore SIGINT for now.
99  signal(SIGINT, sigint);
100  setsid();
101 
102  // Make sure we still have the terminal, though.
103  ioctl(1, TIOCSCTTY, 0);
104 
105  // Set ourselves as the terminal's foreground process group.
106  tcsetpgrp(1, getpgrp());
107 
108  // Get/fix $TERM.
109  const char *TERM = getenv("TERM");
110  if (!TERM)
111  {
112  TERM = "pedigree";
113  setenv("TERM", TERM, 1);
114  }
115 
116  const char *envLcAll = getenv("LC_ALL");
117  if (!envLcAll)
118  {
119  envLcAll = "en_US.UTF-8";
120  setenv("LC_ALL", envLcAll, 1);
121  }
122 
123  // Turn on output processing if it's not already on (we depend on it)
124  struct termios curt;
125  tcgetattr(1, &curt);
126  if (!(curt.c_oflag & OPOST))
127  curt.c_oflag |= OPOST;
128  tcsetattr(1, TCSANOW, &curt);
129 
130  while (1)
131  {
132  // Clear screen before from a previous session before we do anything
133  // else.
134  printf("\033[2J");
135 
136  // Write the login greeting.
137  printf(gettext("Welcome to Pedigree\n"));
138 
139  // Set terminal title, if we can.
140  if (!strcmp(TERM, "xterm"))
141  {
142  printf("\033]0;");
143  printf(gettext("Pedigree Login"));
144  printf("\007");
145  }
146 
147  // Not running anything
148  g_RunningPid = -1;
149 
150  // This handles the case where a bad character goes into the stream and
151  // is impossible to get out. Everything else I've tried does not work...
152  close(0);
153  int fd = open("/dev/tty", 0);
154  if (fd != 0)
155  dup2(fd, 0);
156 
157  // Get username
158  printf(gettext("Username: "));
159 
160 #ifdef FORCE_LOGIN_USER
161  const char *username = FORCE_LOGIN_USER;
162  printf("%s\n", username);
163 #else
164  fflush(stdout);
165 
166  char buffer[256];
167  char *username = fgets(buffer, 256, stdin);
168  if (!username)
169  {
170  continue;
171  }
172 
173  // Knock off the newline character
174  username[strlen(username) - 1] = '\0';
175  if (!strlen(username))
176  {
177  continue;
178  }
179 #endif
180 
181  struct passwd *pw = getpwnam(username);
182  if (!pw)
183  {
184  printf(gettext("\nUnknown user: '%s'\n"), username);
185  continue;
186  }
187 
188  // Get password
189  printf(gettext("Password: "));
190 #ifdef FORCE_LOGIN_PASS
191  const char *password = FORCE_LOGIN_PASS;
192  printf(gettext("(forced)\n"));
193 #else
194  // Use own way - display *
195  fflush(stdout);
196  char password[256], c;
197  int i = 0;
198 
199  tcgetattr(0, &curt);
200  curt.c_lflag &= ~(ECHO | ICANON);
201  tcsetattr(0, TCSANOW, &curt);
202  while (i < 256 && (c = getchar()) != '\n')
203  {
204  if (!c)
205  {
206  continue;
207  }
208  else if (c == '\b')
209  {
210  if (i > 0)
211  {
212  password[--i] = '\0';
213  printf("\b \b");
214  }
215  }
216  else if (c != '\033')
217  {
218  password[i++] = c;
219  if (!strcmp(TERM, "xterm"))
220  printf("•");
221  else
222  printf("*");
223  }
224  }
225  tcgetattr(0, &curt);
226  curt.c_lflag |= (ECHO | ICANON);
227  tcsetattr(0, TCSANOW, &curt);
228  printf("\n");
229 
230  password[i] = '\0';
231 #endif
232 
233  // Perform login - this function is in glue.c.
234  if (pedigree_login(pw->pw_uid, password) != 0)
235  {
236  printf(gettext("Password incorrect.\n"));
237  continue;
238  }
239  else
240  {
241  // Terminal title -> shell name.
242  if (!strcmp(TERM, "xterm"))
243  printf("\033]0;%s\007", pw->pw_shell);
244 
245  // Successful login.
246  struct utmpx *p = 0;
247  setutxent();
248  do
249  {
250  p = getutxent();
251  if (p && (p->ut_type == LOGIN_PROCESS && p->ut_pid == getpid()))
252  break;
253  } while (p);
254 
255  if (p)
256  {
257  struct utmpx ut;
258  memcpy(&ut, p, sizeof(ut));
259 
260  struct timeval tv;
261  gettimeofday(&tv, NULL);
262  ut.ut_tv = tv;
263  ut.ut_type = USER_PROCESS;
264  strncpy(ut.ut_user, pw->pw_name, UT_NAMESIZE);
265 
266  setutxent();
267  pututxline(&ut);
268  }
269  endutxent();
270 
271  // Logged in successfully - launch the shell.
272  int pid;
273  pid = g_RunningPid = fork();
274 
275  if (pid == -1)
276  {
277  perror("fork");
278  exit(errno);
279  }
280  else if (pid == 0)
281  {
282  // Child...
283  g_RunningPid = -1;
284 
285  // Environment - only pass certain variables to the new process.
286  char *newenv[4];
287  newenv[0] = (char *) malloc(256);
288  newenv[1] = (char *) malloc(256);
289  newenv[2] = (char *) malloc(256);
290  newenv[3] = 0;
291 
292  sprintf(newenv[0], "HOME=%s", pw->pw_dir);
293  sprintf(newenv[1], "TERM=%s", TERM);
294  sprintf(newenv[2], "LC_ALL=%s", envLcAll);
295 
296  // Make sure we're starting a login shell.
297  char *shell = (char *) malloc(strlen(pw->pw_shell) + 1);
298  sprintf(shell, "-%s", pw->pw_shell);
299 
300  // Child.
301  execle(pw->pw_shell, shell, 0, newenv);
302 
303  // If we got here, the exec failed.
304  perror("execve");
305  exit(1);
306  }
307  else
308  {
309  // Parent.
310  int stat;
311  waitpid(pid, &stat, 0);
312 
313  g_RunningPid = -1;
314 
315  continue;
316  }
317  }
318  }
319 
320  return 0;
321 }