1/*
2 * Copyright 2007, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 */
8
9
10#include <SupportDefs.h>
11
12#include <errno.h>
13#include <pwd.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <syslog.h>
19#include <unistd.h>
20
21#include "multiuser_utils.h"
22
23
24extern const char* __progname;
25const char* kProgramName = __progname;
26
27const uint32 kRetries = 3;
28const uint32 kTimeout = 60;
29
30
31static status_t
32read_string(char* string, size_t bufferSize)
33{
34	// TODO: setup timeout handler
35
36	// read everything until the next carriage return
37	int c;
38	while ((c = fgetc(stdin)) != EOF && c != '\r' && c != '\n') {
39		if (bufferSize > 1) {
40			string[0] = c;
41			string++;
42			bufferSize--;
43		}
44	}
45
46	if (ferror(stdin) != 0)
47		return ferror(stdin);
48
49	string[0] = '\0';
50	return B_OK;
51}
52
53
54static status_t
55login(const char* user, struct passwd** _passwd)
56{
57	char userBuffer[64];
58
59	if (user == NULL) {
60		char host[64];
61		if (gethostname(host, sizeof(host)) != 0)
62			host[0] = '\0';
63
64		if (host[0])
65			printf("%s ", host);
66		printf("login: ");
67		fflush(stdout);
68
69		status_t status = read_string(userBuffer, sizeof(userBuffer));
70		if (status < B_OK)
71			return status;
72
73		putchar('\n');
74		user = userBuffer;
75	}
76
77	// if no user is given, we exit immediately
78	if (!user[0])
79		exit(1);
80
81	char password[64];
82	status_t status = read_password("password: ", password, sizeof(password),
83		false);
84
85	putchar('\n');
86
87	if (status < B_OK)
88		return status;
89
90	struct passwd* passwd = getpwnam(user);
91	struct spwd* spwd = getspnam(user);
92
93	bool ok = verify_password(passwd, spwd, password);
94	explicit_bzero(password, sizeof(password));
95
96	if (!ok)
97		return B_PERMISSION_DENIED;
98
99	*_passwd = passwd;
100	return B_OK;
101}
102
103
104static const char*
105get_from(const char* host)
106{
107	if (host == NULL)
108		return "";
109
110	static char buffer[64];
111	snprintf(buffer, sizeof(buffer), " from %s", host);
112	return buffer;
113}
114
115
116static void
117usage()
118{
119	fprintf(stderr, "usage: %s [-fp] [-h hostname] [username]\n", kProgramName);
120	exit(1);
121}
122
123
124int
125main(int argc, char *argv[])
126{
127	bool noAuthentification = false;
128	bool preserveEnvironment = false;
129	const char* fromHost = NULL;
130
131	char c;
132	while ((c = getopt(argc, argv, "fh:p")) != -1) {
133		switch (c) {
134			case 'f':
135				noAuthentification = true;
136				break;
137			case 'h':
138				if (geteuid() != 0) {
139					fprintf(stderr, "%s: %s\n", kProgramName,
140						strerror(B_NOT_ALLOWED));
141					exit(1);
142				}
143
144				fromHost = optarg;
145				break;
146			case 'p':
147				preserveEnvironment = true;
148				break;
149
150			default:
151				usage();
152				break;
153		}
154	}
155
156	argc -= optind;
157	argv += optind;
158
159	const char* user = NULL;
160	if (argc > 0)
161		user = argv[0];
162
163	// login
164
165	alarm(kTimeout);
166	openlog(kProgramName, 0, LOG_AUTH);
167
168	uint32 retries = kRetries;
169	status_t status = B_ERROR;
170	struct passwd* passwd = NULL;
171
172	while (retries > 0) {
173		status = login(user, &passwd);
174		if (status == B_OK)
175			break;
176
177		sleep(3);
178		fprintf(stderr, "Login failed.\n");
179		retries--;
180
181		user = NULL;
182			// ask for the user name as well after the first failure
183	}
184
185	alarm(0);
186
187	if (status < B_OK || passwd == NULL) {
188		// login failure
189		syslog(LOG_NOTICE, "login%s failed for \"%s\"", get_from(fromHost),
190			passwd->pw_name);
191		exit(1);
192	}
193
194	// setup environment for the user
195
196	status = setup_environment(passwd, preserveEnvironment);
197	if (status < B_OK) {
198		// refused login
199		fprintf(stderr, "%s: Refused login. Setting up environment failed: %s\n",
200			kProgramName, strerror(status));
201		syslog(LOG_NOTICE, "login%s refused for \"%s\"", get_from(fromHost),
202			passwd->pw_name);
203		exit(1);
204	}
205
206	syslog(LOG_INFO, "login%s as \"%s\"", get_from(fromHost), passwd->pw_name);
207
208	// start login shell
209
210	const char* args[] = {getenv("SHELL"), "-login", NULL};
211	execv(args[0], (char **)args);
212
213	// try default shell
214	args[0] = "/bin/sh";
215	execv(args[0], (char **)args);
216
217	fprintf(stderr, "%s: starting the shell failed: %s", kProgramName,
218		strerror(errno));
219
220	return 1;
221}
222
223