| /* |
| ** 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. |
| */ |
| |
| #include "keystore.h" |
| |
| static inline int has_whitespace(char *name) |
| { |
| if((strrchr(name, ' ') != NULL)) { |
| LOGE("'%s' contains whitespace character\n", name); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int do_list_user_certs(char **arg, char reply[REPLY_MAX]) |
| { |
| return list_user_certs(reply); |
| } |
| |
| static int do_list_ca_certs(char **arg, char reply[REPLY_MAX]) |
| { |
| return list_ca_certs(reply); |
| } |
| |
| static int do_install_user_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| /* copy the certificate and key to keystore */ |
| return install_user_cert(arg[0], arg[1], arg[2]); |
| } |
| |
| static int do_install_p12_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| return install_p12_cert(arg[0], arg[1]); |
| } |
| |
| static int do_install_ca_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| /* copy the certificate and key to keystore */ |
| return install_ca_cert(arg[0], arg[1]); |
| } |
| |
| static int do_add_ca_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| return add_ca_cert(arg[0], arg[1]); |
| } |
| |
| static int do_add_user_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| return add_user_cert(arg[0], arg[1]); |
| } |
| |
| static int do_add_user_key(char **arg, char reply[REPLY_MAX]) |
| { |
| if (has_whitespace(arg[0])) return -1; |
| return add_user_key(arg[0], arg[1]); |
| } |
| |
| static int do_get_ca_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| return get_ca_cert(arg[0], reply); |
| } |
| |
| static int do_get_user_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| return get_user_cert(arg[0], reply); |
| } |
| |
| static int do_get_user_key(char **arg, char reply[REPLY_MAX]) |
| { |
| return get_user_key(arg[0], reply); |
| } |
| |
| static int do_remove_user_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| return remove_user_cert(arg[0]); |
| } |
| |
| static int do_remove_ca_cert(char **arg, char reply[REPLY_MAX]) |
| { |
| return remove_ca_cert(arg[0]); |
| } |
| |
| |
| struct cmdinfo { |
| const char *name; |
| unsigned numargs; |
| int (*func)(char **arg, char reply[REPLY_MAX]); |
| }; |
| |
| |
| struct cmdinfo cmds[] = { |
| { "listcacerts", 0, do_list_ca_certs }, |
| { "listusercerts", 0, do_list_user_certs }, |
| { "installusercert", 3, do_install_user_cert }, |
| { "installcacert", 2, do_install_ca_cert }, |
| { "installp12cert", 2, do_install_p12_cert }, |
| { "addusercert", 2, do_add_user_cert }, |
| { "adduserkey", 2, do_add_user_key }, |
| { "addcacert", 2, do_add_ca_cert }, |
| { "getusercert", 1, do_get_user_cert }, |
| { "getuserkey", 1, do_get_user_key }, |
| { "getcacert", 1, do_get_ca_cert }, |
| { "removecacert", 1, do_remove_ca_cert }, |
| { "removeusercert", 1, do_remove_user_cert }, |
| }; |
| |
| static int readx(int s, void *_buf, int count) |
| { |
| char *buf = _buf; |
| int n = 0, r; |
| if (count < 0) return -1; |
| while (n < count) { |
| r = read(s, buf + n, count - n); |
| if (r < 0) { |
| if (errno == EINTR) continue; |
| LOGE("read error: %s\n", strerror(errno)); |
| return -1; |
| } |
| if (r == 0) { |
| LOGE("eof\n"); |
| return -1; /* EOF */ |
| } |
| n += r; |
| } |
| return 0; |
| } |
| |
| static int writex(int s, const void *_buf, int count) |
| { |
| const char *buf = _buf; |
| int n = 0, r; |
| if (count < 0) return -1; |
| while (n < count) { |
| r = write(s, buf + n, count - n); |
| if (r < 0) { |
| if (errno == EINTR) continue; |
| LOGE("write error: %s\n", strerror(errno)); |
| return -1; |
| } |
| n += r; |
| } |
| return 0; |
| } |
| |
| |
| /* Tokenize the command buffer, locate a matching command, |
| * ensure that the required number of arguments are provided, |
| * call the function(), return the result. |
| */ |
| static int execute(int s, char cmd[BUFFER_MAX]) |
| { |
| char reply[REPLY_MAX]; |
| char *arg[TOKEN_MAX+1]; |
| unsigned i; |
| unsigned n = 0; |
| unsigned short count; |
| short ret = -1; |
| |
| /* default reply is "" */ |
| reply[0] = 0; |
| |
| /* n is number of args (not counting arg[0]) */ |
| arg[0] = cmd; |
| while (*cmd) { |
| if (*cmd == CMD_DELIMITER) { |
| *cmd++ = 0; |
| n++; |
| arg[n] = cmd; |
| if (n == TOKEN_MAX) { |
| LOGE("too many arguments\n"); |
| goto done; |
| } |
| } |
| cmd++; |
| } |
| |
| for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { |
| if (!strcmp(cmds[i].name,arg[0])) { |
| if (n != cmds[i].numargs) { |
| LOGE("%s requires %d arguments (%d given)\n", |
| cmds[i].name, cmds[i].numargs, n); |
| } else { |
| ret = (short) cmds[i].func(arg + 1, reply); |
| } |
| goto done; |
| } |
| } |
| LOGE("unsupported command '%s'\n", arg[0]); |
| |
| done: |
| if (reply[0]) { |
| strlcpy(cmd, reply, BUFFER_MAX); |
| count = strlen(cmd); |
| } else { |
| count = 0; |
| } |
| if (writex(s, &ret, sizeof(ret))) return -1; |
| if (ret == 0) { |
| if (writex(s, &count, sizeof(count))) return -1; |
| if (writex(s, cmd, count)) return -1; |
| } |
| |
| return 0; |
| } |
| |
| int shell_command(const int argc, const char **argv) |
| { |
| int fd, i; |
| short ret; |
| unsigned short count; |
| char delimiter[2] = { CMD_DELIMITER, 0 }; |
| char buf[BUFFER_MAX]=""; |
| |
| 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); |
| } |
| for(i = 0; i < argc; i++) { |
| if (i > 0) strlcat(buf, delimiter, BUFFER_MAX); |
| if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) { |
| fprintf(stderr, "Arguments are too long\n"); |
| exit(1); |
| } |
| } |
| count = strlen(buf); |
| if (writex(fd, &count, sizeof(count))) return -1; |
| if (writex(fd, buf, strlen(buf))) return -1; |
| if (readx(fd, &ret, sizeof(ret))) return -1; |
| if (ret == 0) { |
| if (readx(fd, &count, sizeof(count))) return -1; |
| if (readx(fd, buf, count)) return -1; |
| buf[count]=0; |
| fprintf(stdout, "%s\n", buf); |
| } else { |
| fprintf(stderr, "Failed, please check log!\n"); |
| } |
| return 0; |
| } |
| |
| int main(const int argc, const char *argv[]) |
| { |
| char buf[BUFFER_MAX]; |
| struct sockaddr addr; |
| socklen_t alen; |
| int lsocket, s, count; |
| |
| if (argc > 1) { |
| return shell_command(argc - 1, argv + 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); |
| |
| for (;;) { |
| alen = sizeof(addr); |
| s = accept(lsocket, &addr, &alen); |
| if (s < 0) { |
| LOGE("Accept failed: %s\n", strerror(errno)); |
| continue; |
| } |
| fcntl(s, F_SETFD, FD_CLOEXEC); |
| |
| LOGI("new connection\n"); |
| for (;;) { |
| unsigned short count; |
| if (readx(s, &count, sizeof(count))) { |
| LOGE("failed to read size\n"); |
| break; |
| } |
| if ((count < 1) || (count >= BUFFER_MAX)) { |
| LOGE("invalid size %d\n", count); |
| break; |
| } |
| if (readx(s, buf, count)) { |
| LOGE("failed to read command\n"); |
| break; |
| } |
| buf[count] = 0; |
| if (execute(s, buf)) break; |
| } |
| LOGI("closing connection\n"); |
| close(s); |
| } |
| |
| return 0; |
| } |