7338f8245e3a9bbe47e821fb43c2b699b9527ffb
[grub.git] / grub-core / normal / auth.c
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>
25 #include <grub/time.h>
26 #include <grub/i18n.h>
27
28 struct grub_auth_user
29 {
30   struct grub_auth_user *next;
31   struct grub_auth_user **prev;
32   char *name;
33   grub_auth_callback_t callback;
34   void *arg;
35   int authenticated;
36 };
37
38 static struct grub_auth_user *users = NULL;
39
40 grub_err_t
41 grub_auth_register_authentication (const char *user,
42                                    grub_auth_callback_t callback,
43                                    void *arg)
44 {
45   struct grub_auth_user *cur;
46
47   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
48   if (!cur)
49     cur = grub_zalloc (sizeof (*cur));
50   if (!cur)
51     return grub_errno;
52   cur->callback = callback;
53   cur->arg = arg;
54   if (! cur->name)
55     {
56       cur->name = grub_strdup (user);
57       if (!cur->name)
58         {
59           grub_free (cur);
60           return grub_errno;
61         }
62       grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
63     }
64   return GRUB_ERR_NONE;
65 }
66
67 grub_err_t
68 grub_auth_unregister_authentication (const char *user)
69 {
70   struct grub_auth_user *cur;
71   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
72   if (!cur)
73     return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
74   if (!cur->authenticated)
75     {
76       grub_free (cur->name);
77       grub_list_remove (GRUB_AS_LIST (cur));
78       grub_free (cur);
79     }
80   else
81     {
82       cur->callback = NULL;
83       cur->arg = NULL;
84     }
85   return GRUB_ERR_NONE;
86 }
87
88 grub_err_t
89 grub_auth_authenticate (const char *user)
90 {
91   struct grub_auth_user *cur;
92
93   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
94   if (!cur)
95     cur = grub_zalloc (sizeof (*cur));
96   if (!cur)
97     return grub_errno;
98
99   cur->authenticated = 1;
100
101   if (! cur->name)
102     {
103       cur->name = grub_strdup (user);
104       if (!cur->name)
105         {
106           grub_free (cur);
107           return grub_errno;
108         }
109       grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
110     }
111
112   return GRUB_ERR_NONE;
113 }
114
115 grub_err_t
116 grub_auth_deauthenticate (const char *user)
117 {
118   struct grub_auth_user *cur;
119   cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
120   if (!cur)
121     return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
122   if (!cur->callback)
123     {
124       grub_free (cur->name);
125       grub_list_remove (GRUB_AS_LIST (cur));
126       grub_free (cur);
127     }
128   else
129     cur->authenticated = 0;
130   return GRUB_ERR_NONE;
131 }
132
133 static int
134 is_authenticated (const char *userlist)
135 {
136   const char *superusers;
137   struct grub_auth_user *user;
138
139   superusers = grub_env_get ("superusers");
140
141   if (!superusers)
142     return 1;
143
144   FOR_LIST_ELEMENTS (user, users)
145     {
146       if (!(user->authenticated))
147         continue;
148
149       if ((userlist && grub_strword (userlist, user->name))
150           || grub_strword (superusers, user->name))
151         return 1;
152     }
153
154   return 0;
155 }
156
157 static int
158 grub_username_get (char buf[], unsigned buf_size)
159 {
160   unsigned cur_len = 0;
161   int key;
162
163   while (1)
164     {
165       key = grub_getkey (); 
166       if (key == '\n' || key == '\r')
167         break;
168
169       if (key == '\e')
170         {
171           cur_len = 0;
172           break;
173         }
174
175       if (key == '\b')
176         {
177           if (cur_len)
178             {
179               cur_len--;
180               grub_printf ("\b \b");
181             }
182           continue;
183         }
184
185       if (!grub_isprint (key))
186         continue;
187
188       if (cur_len + 2 < buf_size)
189         {
190           buf[cur_len++] = key;
191           grub_printf ("%c", key);
192         }
193     }
194
195   grub_memset (buf + cur_len, 0, buf_size - cur_len);
196
197   grub_xputs ("\n");
198   grub_refresh ();
199
200   return (key != '\e');
201 }
202
203 grub_err_t
204 grub_auth_check_authentication (const char *userlist)
205 {
206   char login[1024];
207   struct grub_auth_user *cur = NULL;
208   static unsigned long punishment_delay = 1;
209   char entered[GRUB_AUTH_MAX_PASSLEN];
210   struct grub_auth_user *user;
211
212   grub_memset (login, 0, sizeof (login));
213
214   if (is_authenticated (userlist))
215     {
216       punishment_delay = 1;
217       return GRUB_ERR_NONE;
218     }
219
220   grub_puts_ (N_("Enter username: "));
221
222   if (!grub_username_get (login, sizeof (login) - 1))
223     goto access_denied;
224
225   grub_puts_ (N_("Enter password: "));
226
227   if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN))
228     goto access_denied;
229
230   FOR_LIST_ELEMENTS (user, users)
231     {
232       if (grub_strcmp (login, user->name) == 0)
233         cur = user;
234     }
235
236   if (!cur || ! cur->callback)
237     goto access_denied;
238
239   cur->callback (login, entered, cur->arg);
240   if (is_authenticated (userlist))
241     {
242       punishment_delay = 1;
243       return GRUB_ERR_NONE;
244     }
245
246  access_denied:
247   grub_sleep (punishment_delay);
248
249   if (punishment_delay < GRUB_ULONG_MAX / 2)
250     punishment_delay *= 2;
251
252   return GRUB_ACCESS_DENIED;
253 }
254
255 static grub_err_t
256 grub_cmd_authenticate (struct grub_command *cmd __attribute__ ((unused)),
257                        int argc, char **args)
258 {
259   return grub_auth_check_authentication ((argc >= 1) ? args[0] : "");
260 }
261
262 static grub_command_t cmd;
263
264 void
265 grub_normal_auth_init (void)
266 {
267   cmd = grub_register_command ("authenticate",
268                                grub_cmd_authenticate,
269                                N_("[USERLIST]"),
270                                N_("Check whether user is in USERLIST."));
271
272 }
273
274 void
275 grub_normal_auth_fini (void)
276 {
277   grub_unregister_command (cmd);
278 }