blob: eac455e0c5447530a0ae33a95edae89315f61315 [file] [log] [blame]
Jean-Baptiste Queru4d3b5c12009-07-21 11:16:54 -07001/*
2** Copyright 2009, The Android Open Source Project
3**
4** Licensed under the Apache License, Version 2.0 (the "License");
5** you may not use this file except in compliance with the License.
6** You may obtain a copy of the License at
7**
8** http://www.apache.org/licenses/LICENSE-2.0
9**
10** Unless required by applicable law or agreed to in writing, software
11** distributed under the License is distributed on an "AS IS" BASIS,
12** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13** See the License for the specific language governing permissions and
14** limitations under the License.
15*/
16
17#define LOG_TAG "keystore"
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/stat.h>
22#include <dirent.h>
23#include <unistd.h>
24#include <ctype.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <utime.h>
28#include <sys/socket.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31#include <private/android_filesystem_config.h>
32
33#include <cutils/sockets.h>
34#include <cutils/log.h>
35#include <cutils/properties.h>
36
37#include "netkeystore.h"
38#include "keymgmt.h"
39
40#define CMD_PUT_WITH_FILE "putfile"
41
42typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply);
43
44struct cmdinfo {
45 const char *name;
46 CMD_FUNC *func;
47};
48
49static CMD_FUNC do_lock;
50static CMD_FUNC do_unlock;
51static CMD_FUNC do_passwd;
52static CMD_FUNC do_get_state;;
53static CMD_FUNC do_listkeys;
54static CMD_FUNC do_get_key;
55static CMD_FUNC do_put_key;
56static CMD_FUNC do_remove_key;
57static CMD_FUNC do_reset_keystore;
58
59#define str(x) #x
60
61struct cmdinfo cmds[] = {
62 { str(LOCK), do_lock },
63 { str(UNLOCK), do_unlock },
64 { str(PASSWD), do_passwd },
65 { str(GETSTATE), do_get_state },
66 { str(LISTKEYS), do_listkeys },
67 { str(GET), do_get_key },
68 { str(PUT), do_put_key },
69 { str(REMOVE), do_remove_key },
70 { str(RESET), do_reset_keystore },
71};
72
73static struct ucred cr;
74
75static int check_get_perm(int uid)
76{
77 if (uid == AID_WIFI || uid == AID_VPN) return 0;
78 return -1;
79}
80
81static int check_reset_perm(int uid)
82{
83 if (uid == AID_SYSTEM) return 0;
84 return -1;
85}
86
87static int parse_keyname(char *name, uint32_t len,
88 char *namespace, char *keyname)
89{
90 int count = 0;
91 char *c = namespace, *p = namespace, *t = name;
92
93 if (!name || !namespace || !keyname) return -1;
94 while (t < name + len && (*t != 0)) {
95 if (*t == ' ') {
96 if (c == keyname) return -1;
97 *p = count = 0;
98 c = p = keyname;
99 t++;
100 } else {
101 if (!isalnum(*t)) return -1;
102 *p++ = *t++;
103 // also check if the keyname/namespace is too long.
104 if (count++ == MAX_KEY_NAME_LENGTH) return -1;
105 }
106 }
107 *p = 0;
108 return 0;
109}
110
111// args of passwd():
112// firstPassword - for the first time
113// oldPassword newPassword - for changing the password
114static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
115{
116 reply->retcode = passwd((char*)cmd->data);
117}
118
119// args of lock():
120// no argument
121static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
122{
123 reply->retcode = lock();
124}
125
126// args of unlock():
127// password
128static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
129{
130 reply->retcode = unlock((char*)cmd->data);
131}
132
133// args of get_state():
134// no argument
135static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
136{
137 reply->retcode = get_state();
138}
139
140// args of listkeys():
141// namespace
142static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
143{
144 reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data);
145 if (!reply->retcode) reply->len = strlen((char*)reply->data);
146}
147
148// args of get():
149// namespace keyname
150static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
151{
152 char namespace[MAX_KEY_NAME_LENGTH];
153 char keyname[MAX_KEY_NAME_LENGTH];
154
155 if (check_get_perm(cr.uid)) {
156 LOGE("uid %d doesn't have the permission to get key value\n", cr.uid);
157 reply->retcode = -1;
158 return;
159 }
160
161 if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
162 reply->retcode = -1;
163 } else {
164 reply->retcode = get_key(namespace, keyname, reply->data,
165 (int*)&reply->len);
166 }
167}
168
169static int get_value_index(LPC_MARSHAL *cmd)
170{
171 uint32_t count = 0, i;
172 for (i = 0 ; i < cmd->len ; ++i) {
173 if (cmd->data[i] == ' ') {
174 if (++count == 2) return ++i;
175 }
176 }
177 return -1;
178}
179
180// args of put():
181// namespace keyname keyvalue
182static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
183{
184 char namespace[MAX_KEY_NAME_LENGTH];
185 char keyname[MAX_KEY_NAME_LENGTH];
186
187 int p = get_value_index(cmd);
188 if (p == -1) {
189 reply->retcode = -1;
190 } else {
191 unsigned char *value;
192 if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) {
193 reply->retcode = -1;
194 return;
195 }
196 value = &cmd->data[p];
197 int len = cmd->len - p;
198 reply->retcode = put_key(namespace, keyname, value, len);
199 }
200}
201
202// args of remove_key():
203// namespace keyname
204static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
205{
206 char namespace[MAX_KEY_NAME_LENGTH];
207 char keyname[MAX_KEY_NAME_LENGTH];
208 if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
209 reply->retcode = -1;
210 return;
211 }
212 reply->retcode = remove_key(namespace, keyname);
213}
214
215// args of reset_keystore():
216// no argument
217static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
218{
219 if (check_reset_perm(cr.uid)) {
220 LOGE("uid %d doesn't have the permission to reset the keystore\n",
221 cr.uid);
222 reply->retcode = -1;
223 return;
224 }
225 reply->retcode = reset_keystore();
226}
227
228static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
229{
230 uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo);
231
232 if (cmd->opcode >= cmd_max) {
233 LOGE("the opcode (%d) is not valid", cmd->opcode);
234 reply->retcode = -1;
235 return;
236 }
237 cmds[cmd->opcode].func(cmd, reply);
238}
239
240static int set_read_timeout(int socket)
241{
242 struct timeval tv;
243 tv.tv_sec = READ_TIMEOUT;
244 if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv))
245 {
246 LOGE("setsockopt failed");
247 return -1;
248 }
249 return 0;
250}
251
252static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd)
253{
254 int fd, len, ret = 0;
255
256 // get opcode of the function put()
257 if ((fd = open(filename, O_RDONLY)) == -1) {
258 fprintf(stderr, "Can not open file %s\n", filename);
259 return -1;
260 }
261 cmd->data[cmd->len] = ' ';
262 cmd->len++;
263 len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len);
264 if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) {
265 ret = -1;
266 } else {
267 cmd->len += len;
268 }
269 close(fd);
270 return ret;
271}
272
273static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd)
274{
275 int i, len = 0;
276 char *buf = (char*)cmd->data;
277 buf[0] = 0;
278 for (i = 0 ; i < argc ; ++i) {
279 if (i == 0) {
280 len = strlcpy(buf, argv[i], BUFFER_MAX);
281 } else {
282 len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]);
283 }
284 if (len >= BUFFER_MAX) return -1;
285 }
286 if (len) cmd->len = len;
287 return 0;
288}
289
290static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd)
291{
292 uint32_t i, len = 0;
293 uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]);
294
295 for (i = 0 ; i < cmd_max ; ++i) {
296 if (!strcasecmp(argv[0], cmds[i].name)) break;
297 }
298
299 if (i == cmd_max) {
300 // check if this is a command to put the key value with a file.
301 if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1;
302 cmd->opcode = PUT;
303 if (argc != 4) {
304 fprintf(stderr, "%s args\n\tnamespace keyname filename\n",
305 argv[0]);
306 return -1;
307 }
308 if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1;
309 return append_input_from_file(argv[3], cmd);
310 } else {
311 cmd->opcode = i;
312 return flatten_str_args(argc - 1, argv + 1, cmd);
313 }
314}
315
316static int shell_command(const int argc, const char **argv)
317{
318 int fd, i;
319 LPC_MARSHAL cmd;
320
321 if (parse_cmd(argc, argv , &cmd)) {
322 fprintf(stderr, "Incorrect command or command line is too long.\n");
323 exit(1);
324 }
325 fd = socket_local_client(SOCKET_PATH,
326 ANDROID_SOCKET_NAMESPACE_RESERVED,
327 SOCK_STREAM);
328 if (fd == -1) {
329 fprintf(stderr, "Keystore service is not up and running.\n");
330 exit(1);
331 }
332
333 if (write_marshal(fd, &cmd)) {
334 fprintf(stderr, "Incorrect command or command line is too long.\n");
335 exit(1);
336 }
337 if (read_marshal(fd, &cmd)) {
338 fprintf(stderr, "Failed to read the result.\n");
339 exit(1);
340 }
341 cmd.data[cmd.len] = 0;
342 fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!");
343 if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data);
344 close(fd);
345 return 0;
346}
347
348int main(const int argc, const char *argv[])
349{
350 struct sockaddr addr;
351 socklen_t alen;
352 int lsocket, s;
353 LPC_MARSHAL cmd, reply;
354
355 if (argc > 1) {
356 return shell_command(argc - 1, argv + 1);
357 }
358
359 if (init_keystore(KEYSTORE_DIR)) {
360 LOGE("Can not initialize the keystore, the directory exist?\n");
361 exit(1);
362 }
363
364 lsocket = android_get_control_socket(SOCKET_PATH);
365 if (lsocket < 0) {
366 LOGE("Failed to get socket from environment: %s\n", strerror(errno));
367 exit(1);
368 }
369 if (listen(lsocket, 5)) {
370 LOGE("Listen on socket failed: %s\n", strerror(errno));
371 exit(1);
372 }
373 fcntl(lsocket, F_SETFD, FD_CLOEXEC);
374 memset(&reply, 0, sizeof(LPC_MARSHAL));
375
376 for (;;) {
377 socklen_t cr_size = sizeof(cr);
378 alen = sizeof(addr);
379 s = accept(lsocket, &addr, &alen);
380
381 /* retrieve the caller info here */
382 if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
383 close(s);
384 LOGE("Unable to recieve socket options\n");
385 continue;
386 }
387
388 if (s < 0) {
389 LOGE("Accept failed: %s\n", strerror(errno));
390 continue;
391 }
392 fcntl(s, F_SETFD, FD_CLOEXEC);
393 if (set_read_timeout(s)) {
394 close(s);
395 continue;
396 }
397
398 // read the command, execute and send the result back.
399 if(read_marshal(s, &cmd)) goto err;
400 LOGI("new connection\n");
401 execute(&cmd, &reply);
402 write_marshal(s, &reply);
403err:
404 memset(&reply, 0, sizeof(LPC_MARSHAL));
405 LOGI("closing connection\n");
406 close(s);
407 }
408
409 return 0;
410}