-
Notifications
You must be signed in to change notification settings - Fork 0
/
login.c
129 lines (116 loc) · 3.65 KB
/
login.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright (C) 2021 Benjamin Stürz
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
#define PROG_NAME "login"
#include "config.h"
#if HAVE_SETRESUID
#define _GNU_SOURCE
#endif
#include <sys/utsname.h>
#include <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#include "errprintf.h"
#include "common.h"
#include "clearenv.h"
#if HAVE_CRYPT_H
#include <crypt.h>
#endif
#if HAVE_SHADOW_H
#include <shadow.h>
#endif
static bool check_passwd(struct passwd* user, const char* pwd) {
if (!user) return false;
if (!user->pw_passwd || !*user->pw_passwd) return true;
#if HAVE_SHADOW_H
if (strcmp(user->pw_passwd, "x") == 0) {
struct spwd* shadow = getspnam(user->pw_name);
if (!shadow) return false;
else return strcmp(shadow->sp_pwdp, crypt(pwd, shadow->sp_pwdp)) == 0;
} else
#endif
return strcmp(user->pw_passwd, crypt(pwd, user->pw_passwd)) == 0;
}
static void fix_env(const struct passwd* pw) {
clearenv();
const size_t len_name = strlen(pw->pw_name);
char* user = (char*)malloc(6 + len_name);
memcpy(user, "USER=", 5);
memcpy(user + 5, pw->pw_name, len_name);
user[5 + len_name] = '\0';
putenv(user);
const size_t len_home = strlen(pw->pw_dir);
char* home = (char*)malloc(6 + len_home);
memcpy(home, "HOME=", 5);
memcpy(home + 5, pw->pw_dir, len_home);
home[5 + len_home] = '\0';
putenv(home);
const size_t len_shell = strlen(pw->pw_shell);
char* shell = (char*)malloc(7 + len_shell);
memcpy(shell, "SHELL=", 6);
memcpy(shell + 6, pw->pw_shell, len_shell);
shell[6 + len_shell] = '\0';
putenv(shell);
}
// TODO: implement password-hiding on input
int main(void) {
if (geteuid() != 0) {
fputs("login: must be superuser.\n", stderr);
return 1;
}
const char* hostname = "localhost";
struct utsname un;
if (uname(&un) == 0) hostname = un.nodename;
struct passwd* passwd;
char* user;
char* pwd;
begin:
while (1) {
if (feof(stdin)) return 0;
printf("%s login: ", hostname);
fflush(stdout);
user = readline(stdin);
if (!user) continue;
passwd = getpwnam(user);
if (passwd && !*passwd->pw_passwd) break;
fputs("Password: ", stdout);
fflush(stdout);
pwd = readline(stdin);
if (check_passwd(passwd, pwd)) break;
puts("\nLogin incorrect");
buf_free(user);
buf_free(pwd);
}
buf_free(user);
buf_free(pwd);
bool failed;
if (passwd->pw_uid != getuid()) {
#if HAVE_SETRESUID
failed = setresgid(passwd->pw_gid, passwd->pw_gid, passwd->pw_gid) != 0 || initgroups(passwd->pw_name, passwd->pw_gid) != 0 || setresuid(passwd->pw_uid, passwd->pw_uid, passwd->pw_uid) != 0;
#else
failed = setreuid(passwd->pw_uid, passwd->pw_uid) != 0 || setregid(passwd->pw_gid, passwd->pw_gid) != 0;
#endif
if (failed) {
errprintf("failed to setuid or setgid");
goto begin;
}
}
fix_env(passwd);
execl(passwd->pw_shell, "-", NULL);
errprintf("failed to launch login shell");
goto begin;
}