/* ** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #define LOG_TAG "keystore" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netkeystore.h" #include "keymgmt.h" #define CMD_PUT_WITH_FILE "putfile" typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply); struct cmdinfo { const char *name; CMD_FUNC *func; }; static CMD_FUNC do_lock; static CMD_FUNC do_unlock; static CMD_FUNC do_passwd; static CMD_FUNC do_get_state;; static CMD_FUNC do_listkeys; static CMD_FUNC do_get_key; static CMD_FUNC do_put_key; static CMD_FUNC do_remove_key; static CMD_FUNC do_reset_keystore; #define str(x) #x struct cmdinfo cmds[] = { { str(LOCK), do_lock }, { str(UNLOCK), do_unlock }, { str(PASSWD), do_passwd }, { str(GETSTATE), do_get_state }, { str(LISTKEYS), do_listkeys }, { str(GET), do_get_key }, { str(PUT), do_put_key }, { str(REMOVE), do_remove_key }, { str(RESET), do_reset_keystore }, }; static struct ucred cr; static int check_get_perm(int uid) { if (uid == AID_WIFI || uid == AID_VPN) return 0; return -1; } static int check_reset_perm(int uid) { if (uid == AID_SYSTEM) return 0; return -1; } static int parse_keyname(char *name, uint32_t len, char *namespace, char *keyname) { int count = 0; char *c = namespace, *p = namespace, *t = name; if (!name || !namespace || !keyname) return -1; while (t < name + len && (*t != 0)) { if (*t == ' ') { if (c == keyname) return -1; *p = count = 0; c = p = keyname; t++; } else { if (!isalnum(*t)) return -1; *p++ = *t++; // also check if the keyname/namespace is too long. if (count++ == MAX_KEY_NAME_LENGTH) return -1; } } *p = 0; return 0; } // args of passwd(): // firstPassword - for the first time // oldPassword newPassword - for changing the password static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { reply->retcode = passwd((char*)cmd->data); } // args of lock(): // no argument static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { reply->retcode = lock(); } // args of unlock(): // password static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { reply->retcode = unlock((char*)cmd->data); } // args of get_state(): // no argument static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { reply->retcode = get_state(); } // args of listkeys(): // namespace static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data); if (!reply->retcode) reply->len = strlen((char*)reply->data); } // args of get(): // namespace keyname static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { char namespace[MAX_KEY_NAME_LENGTH]; char keyname[MAX_KEY_NAME_LENGTH]; if (check_get_perm(cr.uid)) { LOGE("uid %d doesn't have the permission to get key value\n", cr.uid); reply->retcode = -1; return; } if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { reply->retcode = -1; } else { reply->retcode = get_key(namespace, keyname, reply->data, (int*)&reply->len); } } static int get_value_index(LPC_MARSHAL *cmd) { uint32_t count = 0, i; for (i = 0 ; i < cmd->len ; ++i) { if (cmd->data[i] == ' ') { if (++count == 2) return ++i; } } return -1; } // args of put(): // namespace keyname keyvalue static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { char namespace[MAX_KEY_NAME_LENGTH]; char keyname[MAX_KEY_NAME_LENGTH]; int p = get_value_index(cmd); if (p == -1) { reply->retcode = -1; } else { unsigned char *value; if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) { reply->retcode = -1; return; } value = &cmd->data[p]; int len = cmd->len - p; reply->retcode = put_key(namespace, keyname, value, len); } } // args of remove_key(): // namespace keyname static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { char namespace[MAX_KEY_NAME_LENGTH]; char keyname[MAX_KEY_NAME_LENGTH]; if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) { reply->retcode = -1; return; } reply->retcode = remove_key(namespace, keyname); } // args of reset_keystore(): // no argument static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { if (check_reset_perm(cr.uid)) { LOGE("uid %d doesn't have the permission to reset the keystore\n", cr.uid); reply->retcode = -1; return; } reply->retcode = reset_keystore(); } static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo); if (cmd->opcode >= cmd_max) { LOGE("the opcode (%d) is not valid", cmd->opcode); reply->retcode = -1; return; } cmds[cmd->opcode].func(cmd, reply); } static int set_read_timeout(int socket) { struct timeval tv; tv.tv_sec = READ_TIMEOUT; if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv)) { LOGE("setsockopt failed"); return -1; } return 0; } static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd) { int fd, len, ret = 0; // get opcode of the function put() if ((fd = open(filename, O_RDONLY)) == -1) { fprintf(stderr, "Can not open file %s\n", filename); return -1; } cmd->data[cmd->len] = ' '; cmd->len++; len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len); if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) { ret = -1; } else { cmd->len += len; } close(fd); return ret; } static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd) { int i, len = 0; char *buf = (char*)cmd->data; buf[0] = 0; for (i = 0 ; i < argc ; ++i) { if (i == 0) { len = strlcpy(buf, argv[i], BUFFER_MAX); } else { len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]); } if (len >= BUFFER_MAX) return -1; } if (len) cmd->len = len; return 0; } static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd) { uint32_t i, len = 0; uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]); for (i = 0 ; i < cmd_max ; ++i) { if (!strcasecmp(argv[0], cmds[i].name)) break; } if (i == cmd_max) { // check if this is a command to put the key value with a file. if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1; cmd->opcode = PUT; if (argc != 4) { fprintf(stderr, "%s args\n\tnamespace keyname filename\n", argv[0]); return -1; } if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1; return append_input_from_file(argv[3], cmd); } else { cmd->opcode = i; return flatten_str_args(argc - 1, argv + 1, cmd); } } static int shell_command(const int argc, const char **argv) { int fd, i; LPC_MARSHAL cmd; if (parse_cmd(argc, argv , &cmd)) { fprintf(stderr, "Incorrect command or command line is too long.\n"); exit(1); } fd = socket_local_client(SOCKET_PATH, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if (fd == -1) { fprintf(stderr, "Keystore service is not up and running.\n"); exit(1); } if (write_marshal(fd, &cmd)) { fprintf(stderr, "Incorrect command or command line is too long.\n"); exit(1); } if (read_marshal(fd, &cmd)) { fprintf(stderr, "Failed to read the result.\n"); exit(1); } cmd.data[cmd.len] = 0; fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!"); if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data); close(fd); return 0; } int main(const int argc, const char *argv[]) { struct sockaddr addr; socklen_t alen; int lsocket, s; LPC_MARSHAL cmd, reply; if (argc > 1) { return shell_command(argc - 1, argv + 1); } if (init_keystore(KEYSTORE_DIR)) { LOGE("Can not initialize the keystore, the directory exist?\n"); exit(1); } lsocket = android_get_control_socket(SOCKET_PATH); if (lsocket < 0) { LOGE("Failed to get socket from environment: %s\n", strerror(errno)); exit(1); } if (listen(lsocket, 5)) { LOGE("Listen on socket failed: %s\n", strerror(errno)); exit(1); } fcntl(lsocket, F_SETFD, FD_CLOEXEC); memset(&reply, 0, sizeof(LPC_MARSHAL)); for (;;) { socklen_t cr_size = sizeof(cr); alen = sizeof(addr); s = accept(lsocket, &addr, &alen); /* retrieve the caller info here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); LOGE("Unable to recieve socket options\n"); continue; } if (s < 0) { LOGE("Accept failed: %s\n", strerror(errno)); continue; } fcntl(s, F_SETFD, FD_CLOEXEC); if (set_read_timeout(s)) { close(s); continue; } // read the command, execute and send the result back. if(read_marshal(s, &cmd)) goto err; LOGI("new connection\n"); execute(&cmd, &reply); write_marshal(s, &reply); err: memset(&reply, 0, sizeof(LPC_MARSHAL)); LOGI("closing connection\n"); close(s); } return 0; }