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