The Pedigree Project 0.1
sudo/main.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#include <errno.h>
21#include <pwd.h>
22#include <shadow.h>
23#include <signal.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <termios.h>
28#include <unistd.h>
29
30#include <sys/stat.h>
31#include <sys/wait.h>
32
33// Pedigree function, from libpedigree-c
34extern int pedigree_login(int uid);
35
36static int password_matches(const char* password, const char* stored) {
37 if (!stored || stored[0] == '!' || stored[0] == '*')
38 return 0;
39 if (!stored[0])
40 return !password[0];
41 if (stored[0] == '$') {
42 const char* encrypted = crypt(password, stored);
43 return encrypted && !strcmp(encrypted, stored);
44 }
45
46 return !strcmp(password, stored);
47}
48
49static int wait_for_child(pid_t pid) {
50 int status;
51 while (waitpid(pid, &status, 0) == -1) {
52 if (errno == EINTR)
53 continue;
54
55 fprintf(stderr, "sudo: couldn't wait for child: %s\n", strerror(errno));
56 return errno ? errno : EXIT_FAILURE;
57 }
58
59 if (WIFEXITED(status))
60 return WEXITSTATUS(status);
61 if (WIFSIGNALED(status))
62 return 128 + WTERMSIG(status);
63
64 return EXIT_FAILURE;
65}
66
67int main(int argc, char* argv[]) {
68 int iRunShell = 0, error = 0, help = 0, nStart = 0, i = 0;
69 for (i = 1; i < argc; i++) {
70 if (!strcmp(argv[i], "-s"))
71 iRunShell = 1;
72 else if (!strcmp(argv[i], "-h"))
73 help = 1;
74 else if (!nStart)
75 nStart = i;
76 }
77
78 // If there was an error, or if the help string needs to be printed, do so
79 if (error || help || (!nStart && !iRunShell)) {
80 fprintf(stderr, "Usage: sudo [-h] [-s|<command>]\n");
81 fprintf(stderr, "\n");
82 fprintf(stderr, " -s: Access root shell\n");
83 fprintf(stderr, " -h: Show this help text\n");
84 return error;
85 }
86
87 // Grab the root user's pw structure
88 struct passwd* pw = getpwnam("root");
89 if (!pw) {
90 fprintf(stderr, "sudo: user 'root' doesn't exist!\n");
91 return 1;
92 }
93 struct spwd* sp = getspnam("root");
94 if (!sp) {
95 fprintf(stderr, "sudo: shadow entry for 'root' doesn't exist!\n");
96 return 1;
97 }
98
99 // Request the root password
100 char password[256];
101 int c;
102 size_t passwordLength = 0;
103
104 struct termios curt;
105 tcgetattr(0, &curt);
106 curt.c_lflag &= ~(ECHO | ICANON);
107 tcsetattr(0, TCSANOW, &curt);
108
109 printf("[sudo] Enter password: ");
110 fflush(stdout);
111
112 while ((c = getchar()) != '\n' && c != EOF) {
113 if (c == '\b') {
114 if (passwordLength > 0) {
115 password[--passwordLength] = '\0';
116 printf("\b \b");
117 }
118 } else if (c != '\033' && passwordLength < (sizeof(password) - 1)) {
119 password[passwordLength++] = c;
120 printf("•");
121 }
122 }
123 tcgetattr(0, &curt);
124 curt.c_lflag |= (ECHO | ICANON);
125 tcsetattr(0, TCSANOW, &curt);
126 printf("\n");
127
128 password[passwordLength] = '\0';
129
130 // Attempt to log in as that user
131 if (!password_matches(password, sp->sp_pwdp) || pedigree_login(pw->pw_uid) != 0) {
132 fprintf(stderr, "sudo: password is incorrect\n");
133 return 1;
134 }
135
136 // Begin a new session so SIGINT is properly handled here
137 setsid();
138
139 // We're now running as root, so execute whatever we're supposed to execute
140 if (iRunShell) {
141 // Execute root's shell
142 int pid = fork();
143 if (pid == -1) {
144 fprintf(stderr, "sudo: couldn't fork: %s\n", strerror(errno));
145 exit(errno);
146 } else if (pid == 0) {
147 // Run the command
148 execlp(pw->pw_shell, pw->pw_shell, 0);
149
150 // Command not found!
151 fprintf(stderr, "sudo: couldn't run shell: %s\n", strerror(errno));
152 exit(errno);
153 } else {
154 return wait_for_child(pid);
155 }
156 } else {
157 // Run the command
158 int pid = fork();
159 if (pid == -1) {
160 fprintf(stderr, "sudo: couldn't fork: %s\n", strerror(errno));
161 exit(errno);
162 } else if (pid == 0) {
163 // Run the command
164 execvp(argv[nStart], &argv[nStart]);
165
166 // Command not found!
167 fprintf(stderr, "sudo: couldn't run command '%s': %s\n", argv[nStart], strerror(errno));
168 exit(errno);
169 } else {
170 return wait_for_child(pid);
171 }
172 }
173
174 // All done!
175 return 0;
176}