]>
Commit | Line | Data |
---|---|---|
e7e1f93f | 1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2009 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/auth.h> | |
20 | #include <grub/list.h> | |
21 | #include <grub/mm.h> | |
22 | #include <grub/misc.h> | |
23 | #include <grub/env.h> | |
24 | #include <grub/normal.h> | |
7aea29a3 | 25 | #include <grub/time.h> |
7f39d92f | 26 | #include <grub/i18n.h> |
e7e1f93f | 27 | |
28 | struct grub_auth_user | |
29 | { | |
30 | struct grub_auth_user *next; | |
31 | char *name; | |
32 | grub_auth_callback_t callback; | |
33 | void *arg; | |
34 | int authenticated; | |
35 | }; | |
36 | ||
37 | struct grub_auth_user *users = NULL; | |
38 | ||
39 | int | |
3fd6f044 | 40 | grub_auth_strcmp (const char *s1, const char *s2) |
e7e1f93f | 41 | { |
3fd6f044 RM |
42 | int ret; |
43 | grub_uint64_t end; | |
ec8bb77d | 44 | |
3fd6f044 | 45 | end = grub_get_time_ms () + 100; |
7aea29a3 | 46 | ret = grub_strcmp (s1, s2); |
ec8bb77d | 47 | |
3fd6f044 RM |
48 | /* This prevents an attacker from deriving information about the |
49 | password from the time it took to execute this function. */ | |
50 | while (grub_get_time_ms () < end); | |
e7e1f93f | 51 | |
3fd6f044 | 52 | return ret; |
e7e1f93f | 53 | } |
54 | ||
ff747d50 | 55 | static int |
56 | grub_iswordseparator (int c) | |
57 | { | |
58 | return (grub_isspace (c) || c == ',' || c == ';' || c == '|' || c == '&'); | |
59 | } | |
60 | ||
e7e1f93f | 61 | int |
62 | grub_auth_strword (const char *haystack, const char *needle) | |
63 | { | |
64 | const char *n_pos = needle; | |
65 | int found = 0; | |
66 | ||
67 | while (grub_iswordseparator (*haystack)) | |
68 | haystack++; | |
69 | ||
70 | while (*haystack) | |
71 | { | |
72 | int ok = 1; | |
73 | /* Crawl both the needle and the haystack word we're on. */ | |
74 | while(*haystack && !grub_iswordseparator (*haystack)) | |
75 | { | |
76 | if (*haystack == *n_pos && ok) | |
77 | n_pos++; | |
78 | else | |
79 | ok = 0; | |
80 | ||
81 | haystack++; | |
82 | } | |
83 | ||
84 | if (ok) | |
85 | found = 1; | |
86 | } | |
87 | ||
88 | return found; | |
89 | } | |
90 | ||
91 | grub_err_t | |
92 | grub_auth_register_authentication (const char *user, | |
93 | grub_auth_callback_t callback, | |
94 | void *arg) | |
95 | { | |
96 | struct grub_auth_user *cur; | |
97 | ||
98 | cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user); | |
99 | if (!cur) | |
100 | cur = grub_zalloc (sizeof (*cur)); | |
101 | if (!cur) | |
102 | return grub_errno; | |
103 | cur->callback = callback; | |
104 | cur->arg = arg; | |
105 | if (! cur->name) | |
106 | { | |
107 | cur->name = grub_strdup (user); | |
108 | if (!cur->name) | |
109 | { | |
110 | grub_free (cur); | |
111 | return grub_errno; | |
112 | } | |
113 | grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur)); | |
114 | } | |
115 | return GRUB_ERR_NONE; | |
116 | } | |
117 | ||
118 | grub_err_t | |
119 | grub_auth_unregister_authentication (const char *user) | |
120 | { | |
121 | struct grub_auth_user *cur; | |
122 | cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user); | |
123 | if (!cur) | |
124 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user); | |
125 | if (!cur->authenticated) | |
126 | { | |
127 | grub_free (cur->name); | |
128 | grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur)); | |
129 | grub_free (cur); | |
130 | } | |
131 | else | |
132 | { | |
133 | cur->callback = NULL; | |
134 | cur->arg = NULL; | |
135 | } | |
136 | return GRUB_ERR_NONE; | |
137 | } | |
138 | ||
139 | grub_err_t | |
140 | grub_auth_authenticate (const char *user) | |
141 | { | |
142 | struct grub_auth_user *cur; | |
143 | ||
144 | cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user); | |
145 | if (!cur) | |
146 | cur = grub_zalloc (sizeof (*cur)); | |
147 | if (!cur) | |
148 | return grub_errno; | |
149 | ||
150 | cur->authenticated = 1; | |
151 | ||
152 | if (! cur->name) | |
153 | { | |
154 | cur->name = grub_strdup (user); | |
155 | if (!cur->name) | |
156 | { | |
157 | grub_free (cur); | |
158 | return grub_errno; | |
159 | } | |
160 | grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur)); | |
161 | } | |
162 | ||
163 | return GRUB_ERR_NONE; | |
164 | } | |
165 | ||
166 | grub_err_t | |
167 | grub_auth_deauthenticate (const char *user) | |
168 | { | |
169 | struct grub_auth_user *cur; | |
170 | cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user); | |
171 | if (!cur) | |
172 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user); | |
173 | if (!cur->callback) | |
174 | { | |
175 | grub_free (cur->name); | |
176 | grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur)); | |
177 | grub_free (cur); | |
178 | } | |
179 | else | |
180 | cur->authenticated = 0; | |
181 | return GRUB_ERR_NONE; | |
182 | } | |
183 | ||
184 | static int | |
185 | is_authenticated (const char *userlist) | |
186 | { | |
187 | const char *superusers; | |
188 | ||
189 | auto int hook (grub_list_t item); | |
190 | int hook (grub_list_t item) | |
191 | { | |
192 | const char *name; | |
193 | if (!((struct grub_auth_user *) item)->authenticated) | |
194 | return 0; | |
195 | name = ((struct grub_auth_user *) item)->name; | |
196 | ||
197 | return (userlist && grub_auth_strword (userlist, name)) | |
198 | || grub_auth_strword (superusers, name); | |
199 | } | |
200 | ||
201 | superusers = grub_env_get ("superusers"); | |
202 | ||
203 | if (!superusers) | |
204 | return 1; | |
205 | ||
206 | return grub_list_iterate (GRUB_AS_LIST (users), hook); | |
207 | } | |
208 | ||
209 | grub_err_t | |
210 | grub_auth_check_authentication (const char *userlist) | |
211 | { | |
212 | char login[1024]; | |
213 | struct grub_auth_user *cur = NULL; | |
214 | grub_err_t err; | |
4089b167 | 215 | static unsigned long punishment_delay = 1; |
e7e1f93f | 216 | |
217 | auto int hook (grub_list_t item); | |
218 | int hook (grub_list_t item) | |
219 | { | |
220 | if (grub_auth_strcmp (login, ((struct grub_auth_user *) item)->name) == 0) | |
221 | cur = (struct grub_auth_user *) item; | |
222 | return 0; | |
223 | } | |
224 | ||
225 | auto int hook_any (grub_list_t item); | |
226 | int hook_any (grub_list_t item) | |
227 | { | |
228 | if (((struct grub_auth_user *) item)->callback) | |
229 | cur = (struct grub_auth_user *) item; | |
230 | return 0; | |
231 | } | |
232 | ||
233 | grub_memset (login, 0, sizeof (login)); | |
234 | ||
235 | if (is_authenticated (userlist)) | |
3fd6f044 RM |
236 | { |
237 | punishment_delay = 1; | |
238 | return GRUB_ERR_NONE; | |
239 | } | |
e7e1f93f | 240 | |
7f39d92f | 241 | if (!grub_cmdline_get (N_("Enter username:"), login, sizeof (login) - 1, |
e7e1f93f | 242 | 0, 0, 0)) |
3fd6f044 | 243 | goto access_denied; |
e7e1f93f | 244 | |
245 | grub_list_iterate (GRUB_AS_LIST (users), hook); | |
246 | ||
247 | if (!cur || ! cur->callback) | |
248 | { | |
249 | grub_list_iterate (GRUB_AS_LIST (users), hook_any); | |
250 | ||
251 | /* No users present at all. */ | |
252 | if (!cur) | |
3fd6f044 | 253 | goto access_denied; |
e7e1f93f | 254 | |
255 | /* Display any of available authentication schemes. */ | |
256 | err = cur->callback (login, 0); | |
257 | ||
3fd6f044 | 258 | goto access_denied; |
e7e1f93f | 259 | } |
260 | err = cur->callback (login, cur->arg); | |
261 | if (is_authenticated (userlist)) | |
3fd6f044 RM |
262 | { |
263 | punishment_delay = 1; | |
264 | return GRUB_ERR_NONE; | |
265 | } | |
266 | ||
267 | access_denied: | |
268 | grub_sleep (punishment_delay); | |
269 | ||
270 | if (punishment_delay < GRUB_ULONG_MAX / 2) | |
271 | punishment_delay *= 2; | |
272 | ||
e7e1f93f | 273 | return GRUB_ACCESS_DENIED; |
274 | } |