Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 1 | /* |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 2 | ** Copyright 2009, The Android Open Source Project |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 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 | #include "keystore.h" |
| 18 | |
| 19 | static DIR *open_keystore(const char *dir) |
| 20 | { |
| 21 | DIR *d; |
| 22 | if ((d = opendir(dir)) == NULL) { |
| 23 | if (mkdir(dir, 0770) < 0) { |
| 24 | LOGE("cannot create dir '%s': %s\n", dir, strerror(errno)); |
| 25 | unlink(dir); |
| 26 | return NULL; |
| 27 | } |
| 28 | d = open_keystore(dir); |
| 29 | } |
| 30 | return d; |
| 31 | } |
| 32 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 33 | static int list_files(const char *dir, char reply[REPLY_MAX]) |
| 34 | { |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 35 | struct dirent *de; |
| 36 | DIR *d; |
| 37 | |
| 38 | if ((d = open_keystore(dir)) == NULL) { |
| 39 | return -1; |
| 40 | } |
| 41 | reply[0]=0; |
| 42 | while ((de = readdir(d))) { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 43 | if (de->d_type != DT_DIR) continue; |
| 44 | if ((strcmp(DOT, de->d_name) == 0) || |
| 45 | (strcmp(DOTDOT, de->d_name) == 0)) continue; |
Chung-yih Wang | 8fcbada | 2009-06-10 18:45:14 +0800 | [diff] [blame] | 46 | if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 47 | if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) { |
| 48 | LOGE("reply is too long(too many files under '%s'\n", dir); |
| 49 | return -1; |
| 50 | } |
| 51 | } |
| 52 | closedir(d); |
| 53 | return 0; |
| 54 | } |
| 55 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 56 | static int copy_keyfile(const char *src, int src_type, const char *dstfile) { |
| 57 | int srcfd = -1, dstfd; |
| 58 | char buf[REPLY_MAX]; |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 59 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 60 | if ((src_type == IS_FILE) && (srcfd = open(src, O_RDONLY)) == -1) { |
| 61 | LOGE("Cannot open the original file '%s'\n", src); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 62 | return -1; |
| 63 | } |
| 64 | if ((dstfd = open(dstfile, O_CREAT|O_RDWR)) == -1) { |
| 65 | LOGE("Cannot open the destination file '%s'\n", dstfile); |
| 66 | return -1; |
| 67 | } |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 68 | if (src_type == IS_FILE) { |
| 69 | int length; |
| 70 | while((length = read(srcfd, buf, REPLY_MAX)) > 0) { |
| 71 | write(dstfd, buf, length); |
| 72 | } |
| 73 | } else { |
| 74 | write(dstfd, src, strlen(src)); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 75 | } |
| 76 | close(srcfd); |
| 77 | close(dstfd); |
| 78 | chmod(dstfile, 0440); |
| 79 | return 0; |
| 80 | } |
| 81 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 82 | static int install_key(const char *path, const char *certname, const char *src, |
| 83 | int src_is_file, char *dstfile) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 84 | { |
| 85 | struct dirent *de; |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 86 | char fullpath[KEYNAME_LENGTH]; |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 87 | DIR *d; |
| 88 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 89 | if (snprintf(fullpath, sizeof(fullpath), "%s/%s/", path, certname) |
| 90 | >= KEYNAME_LENGTH) { |
| 91 | LOGE("cert name '%s' is too long.\n", certname); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 92 | return -1; |
| 93 | } |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 94 | |
| 95 | if ((d = open_keystore(fullpath)) == NULL) { |
| 96 | LOGE("Can not open the keystore '%s'\n", fullpath); |
| 97 | return -1; |
| 98 | } |
| 99 | closedir(d); |
| 100 | if (strlcat(fullpath, dstfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) { |
| 101 | LOGE("cert name '%s' is too long.\n", certname); |
| 102 | return -1; |
| 103 | } |
| 104 | return copy_keyfile(src, src_is_file, fullpath); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 105 | } |
| 106 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 107 | static int get_key(const char *path, const char *keyname, const char *file, |
| 108 | char reply[REPLY_MAX]) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 109 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 110 | struct dirent *de; |
| 111 | char filename[KEYNAME_LENGTH]; |
| 112 | int fd; |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 113 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 114 | if (snprintf(filename, sizeof(filename), "%s/%s/%s", path, keyname, file) |
| 115 | >= KEYNAME_LENGTH) { |
| 116 | LOGE("cert name '%s' is too long.\n", keyname); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 117 | return -1; |
| 118 | } |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 119 | |
| 120 | if ((fd = open(filename, O_RDONLY)) == -1) { |
| 121 | return -1; |
| 122 | } |
| 123 | close(fd); |
| 124 | strlcpy(reply, filename, REPLY_MAX); |
| 125 | return 0; |
| 126 | } |
| 127 | |
| 128 | static int remove_key(const char *dir, const char *key) |
| 129 | { |
| 130 | char dstfile[KEYNAME_LENGTH]; |
| 131 | char *keyfile[4] = { USER_KEY, USER_P12_CERT, USER_CERTIFICATE, |
| 132 | CA_CERTIFICATE }; |
| 133 | int i, count = 0; |
| 134 | |
| 135 | for ( i = 0 ; i < 4 ; i++) { |
| 136 | if (snprintf(dstfile, KEYNAME_LENGTH, "%s/%s/%s", dir, key, keyfile[i]) |
| 137 | >= KEYNAME_LENGTH) { |
| 138 | LOGE("keyname is too long '%s'\n", key); |
| 139 | return -1; |
| 140 | } |
| 141 | if (unlink(dstfile) == 0) count++; |
| 142 | } |
| 143 | |
| 144 | if (count == 0) { |
| 145 | LOGE("can not clean up '%s' keys or not exist\n", key); |
| 146 | return -1; |
| 147 | } |
| 148 | |
| 149 | snprintf(dstfile, KEYNAME_LENGTH, "%s/%s", dir, key); |
| 150 | if (rmdir(dstfile)) { |
| 151 | LOGE("can not clean up '%s' directory\n", key); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 152 | return -1; |
| 153 | } |
| 154 | return 0; |
| 155 | } |
| 156 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 157 | int list_user_certs(char reply[REPLY_MAX]) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 158 | { |
| 159 | return list_files(CERTS_DIR, reply); |
| 160 | } |
| 161 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 162 | int list_ca_certs(char reply[REPLY_MAX]) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 163 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 164 | return list_files(CACERTS_DIR, reply); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 165 | } |
| 166 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 167 | int install_user_cert(const char *keyname, const char *cert, const char *key) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 168 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 169 | if (install_key(CERTS_DIR, keyname, cert, IS_FILE, USER_CERTIFICATE) == 0) { |
| 170 | return install_key(CERTS_DIR, keyname, key, IS_FILE, USER_KEY); |
| 171 | } |
| 172 | return -1; |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 173 | } |
| 174 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 175 | int install_ca_cert(const char *keyname, const char *certfile) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 176 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 177 | return install_key(CACERTS_DIR, keyname, certfile, IS_FILE, CA_CERTIFICATE); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 178 | } |
| 179 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 180 | int install_p12_cert(const char *keyname, const char *certfile) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 181 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 182 | return install_key(CERTS_DIR, keyname, certfile, IS_FILE, USER_P12_CERT); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 183 | } |
| 184 | |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 185 | int add_ca_cert(const char *keyname, const char *certificate) |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 186 | { |
Chung-yih Wang | 4c40420 | 2009-06-17 01:29:21 +0800 | [diff] [blame^] | 187 | return install_key(CACERTS_DIR, keyname, certificate, IS_CONTENT, |
| 188 | CA_CERTIFICATE); |
| 189 | } |
| 190 | |
| 191 | int add_user_cert(const char *keyname, const char *certificate) |
| 192 | { |
| 193 | return install_key(CERTS_DIR, keyname, certificate, IS_CONTENT, |
| 194 | USER_CERTIFICATE); |
| 195 | } |
| 196 | |
| 197 | int add_user_key(const char *keyname, const char *key) |
| 198 | { |
| 199 | return install_key(CERTS_DIR, keyname, key, IS_CONTENT, USER_KEY); |
| 200 | } |
| 201 | |
| 202 | int get_ca_cert(const char *keyname, char reply[REPLY_MAX]) |
| 203 | { |
| 204 | return get_key(CACERTS_DIR, keyname, CA_CERTIFICATE, reply); |
| 205 | } |
| 206 | |
| 207 | int get_user_cert(const char *keyname, char reply[REPLY_MAX]) |
| 208 | { |
| 209 | return get_key(CERTS_DIR, keyname, USER_CERTIFICATE, reply); |
| 210 | } |
| 211 | |
| 212 | int get_user_key(const char *keyname, char reply[REPLY_MAX]) |
| 213 | { |
| 214 | if(get_key(CERTS_DIR, keyname, USER_KEY, reply)) |
| 215 | return get_key(CERTS_DIR, keyname, USER_P12_CERT, reply); |
| 216 | return 0; |
| 217 | } |
| 218 | |
| 219 | int remove_user_cert(const char *key) |
| 220 | { |
| 221 | return remove_key(CERTS_DIR, key); |
| 222 | } |
| 223 | |
| 224 | int remove_ca_cert(const char *key) |
| 225 | { |
| 226 | return remove_key(CACERTS_DIR, key); |
Chung-yih Wang | a92d5dc | 2009-06-08 16:34:54 +0800 | [diff] [blame] | 227 | } |