Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | /* |
michael@0 | 5 | * The following code handles the storage of PKCS 11 modules used by the |
michael@0 | 6 | * NSS. For the rest of NSS, only one kind of database handle exists: |
michael@0 | 7 | * |
michael@0 | 8 | * SFTKDBHandle |
michael@0 | 9 | * |
michael@0 | 10 | * There is one SFTKDBHandle for each key database and one for each cert |
michael@0 | 11 | * database. These databases are opened as associated pairs, one pair per |
michael@0 | 12 | * slot. SFTKDBHandles are reference counted objects. |
michael@0 | 13 | * |
michael@0 | 14 | * Each SFTKDBHandle points to a low level database handle (SDB). This handle |
michael@0 | 15 | * represents the underlying physical database. These objects are not |
michael@0 | 16 | * reference counted, and are 'owned' by their respective SFTKDBHandles. |
michael@0 | 17 | */ |
michael@0 | 18 | |
michael@0 | 19 | #include "prprf.h" |
michael@0 | 20 | #include "prsystem.h" |
michael@0 | 21 | #include "secport.h" |
michael@0 | 22 | #include "utilpars.h" |
michael@0 | 23 | #include "secerr.h" |
michael@0 | 24 | |
michael@0 | 25 | #if defined (_WIN32) |
michael@0 | 26 | #include <io.h> |
michael@0 | 27 | #endif |
michael@0 | 28 | #ifdef XP_UNIX |
michael@0 | 29 | #include <unistd.h> |
michael@0 | 30 | #endif |
michael@0 | 31 | |
michael@0 | 32 | #include <sys/types.h> |
michael@0 | 33 | #include <sys/stat.h> |
michael@0 | 34 | #include <fcntl.h> |
michael@0 | 35 | |
michael@0 | 36 | #if defined (_WIN32) |
michael@0 | 37 | #define os_open _open |
michael@0 | 38 | #define os_fdopen _fdopen |
michael@0 | 39 | #define os_stat _stat |
michael@0 | 40 | #define os_truncate_open_flags _O_CREAT|_O_RDWR|_O_TRUNC |
michael@0 | 41 | #define os_append_open_flags _O_CREAT|_O_RDWR|_O_APPEND |
michael@0 | 42 | #define os_open_permissions_type int |
michael@0 | 43 | #define os_open_permissions_default _S_IREAD | _S_IWRITE |
michael@0 | 44 | #define os_stat_type struct _stat |
michael@0 | 45 | #else |
michael@0 | 46 | #define os_open open |
michael@0 | 47 | #define os_fdopen fdopen |
michael@0 | 48 | #define os_stat stat |
michael@0 | 49 | #define os_truncate_open_flags O_CREAT|O_RDWR|O_TRUNC |
michael@0 | 50 | #define os_append_open_flags O_CREAT|O_RDWR|O_APPEND |
michael@0 | 51 | #define os_open_permissions_type mode_t |
michael@0 | 52 | #define os_open_permissions_default 0600 |
michael@0 | 53 | #define os_stat_type struct stat |
michael@0 | 54 | #endif |
michael@0 | 55 | |
michael@0 | 56 | /**************************************************************** |
michael@0 | 57 | * |
michael@0 | 58 | * Secmod database. |
michael@0 | 59 | * |
michael@0 | 60 | * The new secmod database is simply a text file with each of the module |
michael@0 | 61 | * entries in the following form: |
michael@0 | 62 | * |
michael@0 | 63 | * # |
michael@0 | 64 | * # This is a comment The next line is the library to load |
michael@0 | 65 | * library=libmypkcs11.so |
michael@0 | 66 | * name="My PKCS#11 module" |
michael@0 | 67 | * params="my library's param string" |
michael@0 | 68 | * nss="NSS parameters" |
michael@0 | 69 | * other="parameters for other libraries and applications" |
michael@0 | 70 | * |
michael@0 | 71 | * library=libmynextpk11.so |
michael@0 | 72 | * name="My other PKCS#11 module" |
michael@0 | 73 | */ |
michael@0 | 74 | |
michael@0 | 75 | |
michael@0 | 76 | /* |
michael@0 | 77 | * Smart string cat functions. Automatically manage the memory. |
michael@0 | 78 | * The first parameter is the source string. If it's null, we |
michael@0 | 79 | * allocate memory for it. If it's not, we reallocate memory |
michael@0 | 80 | * so the the concanenated string fits. |
michael@0 | 81 | */ |
michael@0 | 82 | static char * |
michael@0 | 83 | nssutil_DupnCat(char *baseString, const char *str, int str_len) |
michael@0 | 84 | { |
michael@0 | 85 | int len = (baseString ? PORT_Strlen(baseString) : 0) + 1; |
michael@0 | 86 | char *newString; |
michael@0 | 87 | |
michael@0 | 88 | len += str_len; |
michael@0 | 89 | newString = (char *) PORT_Realloc(baseString,len); |
michael@0 | 90 | if (newString == NULL) { |
michael@0 | 91 | PORT_Free(baseString); |
michael@0 | 92 | return NULL; |
michael@0 | 93 | } |
michael@0 | 94 | if (baseString == NULL) *newString = 0; |
michael@0 | 95 | return PORT_Strncat(newString,str, str_len); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | /* Same as nssutil_DupnCat except it concatenates the full string, not a |
michael@0 | 99 | * partial one */ |
michael@0 | 100 | static char * |
michael@0 | 101 | nssutil_DupCat(char *baseString, const char *str) |
michael@0 | 102 | { |
michael@0 | 103 | return nssutil_DupnCat(baseString, str, PORT_Strlen(str)); |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | /* function to free up all the memory associated with a null terminated |
michael@0 | 107 | * array of module specs */ |
michael@0 | 108 | static SECStatus |
michael@0 | 109 | nssutil_releaseSpecList(char **moduleSpecList) |
michael@0 | 110 | { |
michael@0 | 111 | if (moduleSpecList) { |
michael@0 | 112 | char **index; |
michael@0 | 113 | for(index = moduleSpecList; *index; index++) { |
michael@0 | 114 | PORT_Free(*index); |
michael@0 | 115 | } |
michael@0 | 116 | PORT_Free(moduleSpecList); |
michael@0 | 117 | } |
michael@0 | 118 | return SECSuccess; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | #define SECMOD_STEP 10 |
michael@0 | 122 | static SECStatus |
michael@0 | 123 | nssutil_growList(char ***pModuleList, int *useCount, int last) |
michael@0 | 124 | { |
michael@0 | 125 | char **newModuleList; |
michael@0 | 126 | |
michael@0 | 127 | *useCount += SECMOD_STEP; |
michael@0 | 128 | newModuleList = (char **)PORT_Realloc(*pModuleList, |
michael@0 | 129 | *useCount*sizeof(char *)); |
michael@0 | 130 | if (newModuleList == NULL) { |
michael@0 | 131 | return SECFailure; |
michael@0 | 132 | } |
michael@0 | 133 | PORT_Memset(&newModuleList[last],0, sizeof(char *)*SECMOD_STEP); |
michael@0 | 134 | *pModuleList = newModuleList; |
michael@0 | 135 | return SECSuccess; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | static |
michael@0 | 139 | char *_NSSUTIL_GetOldSecmodName(const char *dbname,const char *filename) |
michael@0 | 140 | { |
michael@0 | 141 | char *file = NULL; |
michael@0 | 142 | char *dirPath = PORT_Strdup(dbname); |
michael@0 | 143 | char *sep; |
michael@0 | 144 | |
michael@0 | 145 | sep = PORT_Strrchr(dirPath,*NSSUTIL_PATH_SEPARATOR); |
michael@0 | 146 | #ifdef _WIN32 |
michael@0 | 147 | if (!sep) { |
michael@0 | 148 | /* utilparst.h defines NSSUTIL_PATH_SEPARATOR as "/" for all |
michael@0 | 149 | * platforms. */ |
michael@0 | 150 | sep = PORT_Strrchr(dirPath,'\\'); |
michael@0 | 151 | } |
michael@0 | 152 | #endif |
michael@0 | 153 | if (sep) { |
michael@0 | 154 | *sep = 0; |
michael@0 | 155 | file = PR_smprintf("%s"NSSUTIL_PATH_SEPARATOR"%s", dirPath, filename); |
michael@0 | 156 | } else { |
michael@0 | 157 | file = PR_smprintf("%s", filename); |
michael@0 | 158 | } |
michael@0 | 159 | PORT_Free(dirPath); |
michael@0 | 160 | return file; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | static SECStatus nssutil_AddSecmodDBEntry(const char *appName, |
michael@0 | 164 | const char *filename, |
michael@0 | 165 | const char *dbname, |
michael@0 | 166 | char *module, PRBool rw); |
michael@0 | 167 | |
michael@0 | 168 | enum lfopen_mode { lfopen_truncate, lfopen_append }; |
michael@0 | 169 | |
michael@0 | 170 | FILE * |
michael@0 | 171 | lfopen(const char *name, enum lfopen_mode om, os_open_permissions_type open_perms) |
michael@0 | 172 | { |
michael@0 | 173 | int fd; |
michael@0 | 174 | FILE *file; |
michael@0 | 175 | |
michael@0 | 176 | fd = os_open(name, |
michael@0 | 177 | (om == lfopen_truncate) ? os_truncate_open_flags : os_append_open_flags, |
michael@0 | 178 | open_perms); |
michael@0 | 179 | if (fd < 0) { |
michael@0 | 180 | return NULL; |
michael@0 | 181 | } |
michael@0 | 182 | file = os_fdopen(fd, (om == lfopen_truncate) ? "w+" : "a+"); |
michael@0 | 183 | if (!file) { |
michael@0 | 184 | close(fd); |
michael@0 | 185 | } |
michael@0 | 186 | /* file inherits fd */ |
michael@0 | 187 | return file; |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | #define MAX_LINE_LENGTH 2048 |
michael@0 | 191 | |
michael@0 | 192 | /* |
michael@0 | 193 | * Read all the existing modules in out of the file. |
michael@0 | 194 | */ |
michael@0 | 195 | static char ** |
michael@0 | 196 | nssutil_ReadSecmodDB(const char *appName, |
michael@0 | 197 | const char *filename, const char *dbname, |
michael@0 | 198 | char *params, PRBool rw) |
michael@0 | 199 | { |
michael@0 | 200 | FILE *fd = NULL; |
michael@0 | 201 | char **moduleList = NULL; |
michael@0 | 202 | int moduleCount = 1; |
michael@0 | 203 | int useCount = SECMOD_STEP; |
michael@0 | 204 | char line[MAX_LINE_LENGTH]; |
michael@0 | 205 | PRBool internal = PR_FALSE; |
michael@0 | 206 | PRBool skipParams = PR_FALSE; |
michael@0 | 207 | char *moduleString = NULL; |
michael@0 | 208 | char *paramsValue=NULL; |
michael@0 | 209 | PRBool failed = PR_TRUE; |
michael@0 | 210 | |
michael@0 | 211 | moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **)); |
michael@0 | 212 | if (moduleList == NULL) return NULL; |
michael@0 | 213 | |
michael@0 | 214 | if (dbname == NULL) { |
michael@0 | 215 | goto return_default; |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | /* do we really want to use streams here */ |
michael@0 | 219 | fd = fopen(dbname, "r"); |
michael@0 | 220 | if (fd == NULL) goto done; |
michael@0 | 221 | |
michael@0 | 222 | /* |
michael@0 | 223 | * the following loop takes line separated config lines and collapses |
michael@0 | 224 | * the lines to a single string, escaping and quoting as necessary. |
michael@0 | 225 | */ |
michael@0 | 226 | /* loop state variables */ |
michael@0 | 227 | moduleString = NULL; /* current concatenated string */ |
michael@0 | 228 | internal = PR_FALSE; /* is this an internal module */ |
michael@0 | 229 | skipParams = PR_FALSE; /* did we find an override parameter block*/ |
michael@0 | 230 | paramsValue = NULL; /* the current parameter block value */ |
michael@0 | 231 | while (fgets(line, sizeof(line), fd) != NULL) { |
michael@0 | 232 | int len = PORT_Strlen(line); |
michael@0 | 233 | |
michael@0 | 234 | /* remove the ending newline */ |
michael@0 | 235 | if (len && line[len-1] == '\n') { |
michael@0 | 236 | len--; |
michael@0 | 237 | line[len] = 0; |
michael@0 | 238 | } |
michael@0 | 239 | if (*line == '#') { |
michael@0 | 240 | continue; |
michael@0 | 241 | } |
michael@0 | 242 | if (*line != 0) { |
michael@0 | 243 | /* |
michael@0 | 244 | * The PKCS #11 group standard assumes blocks of strings |
michael@0 | 245 | * separated by new lines, clumped by new lines. Internally |
michael@0 | 246 | * we take strings separated by spaces, so we may need to escape |
michael@0 | 247 | * certain spaces. |
michael@0 | 248 | */ |
michael@0 | 249 | char *value = PORT_Strchr(line,'='); |
michael@0 | 250 | |
michael@0 | 251 | /* there is no value, write out the stanza as is */ |
michael@0 | 252 | if (value == NULL || value[1] == 0) { |
michael@0 | 253 | if (moduleString) { |
michael@0 | 254 | moduleString = nssutil_DupnCat(moduleString," ", 1); |
michael@0 | 255 | if (moduleString == NULL) goto loser; |
michael@0 | 256 | } |
michael@0 | 257 | moduleString = nssutil_DupCat(moduleString, line); |
michael@0 | 258 | if (moduleString == NULL) goto loser; |
michael@0 | 259 | /* value is already quoted, just write it out */ |
michael@0 | 260 | } else if (value[1] == '"') { |
michael@0 | 261 | if (moduleString) { |
michael@0 | 262 | moduleString = nssutil_DupnCat(moduleString," ", 1); |
michael@0 | 263 | if (moduleString == NULL) goto loser; |
michael@0 | 264 | } |
michael@0 | 265 | moduleString = nssutil_DupCat(moduleString, line); |
michael@0 | 266 | if (moduleString == NULL) goto loser; |
michael@0 | 267 | /* we have an override parameter section, remember that |
michael@0 | 268 | * we found this (see following comment about why this |
michael@0 | 269 | * is necessary). */ |
michael@0 | 270 | if (PORT_Strncasecmp(line, "parameters", 10) == 0) { |
michael@0 | 271 | skipParams = PR_TRUE; |
michael@0 | 272 | } |
michael@0 | 273 | /* |
michael@0 | 274 | * The internal token always overrides it's parameter block |
michael@0 | 275 | * from the passed in parameters, so wait until then end |
michael@0 | 276 | * before we include the parameter block in case we need to |
michael@0 | 277 | * override it. NOTE: if the parameter block is quoted with ("), |
michael@0 | 278 | * this override does not happen. This allows you to override |
michael@0 | 279 | * the application's parameter configuration. |
michael@0 | 280 | * |
michael@0 | 281 | * parameter block state is controlled by the following variables: |
michael@0 | 282 | * skipParams - Bool : set to true of we have an override param |
michael@0 | 283 | * block (all other blocks, either implicit or explicit are |
michael@0 | 284 | * ignored). |
michael@0 | 285 | * paramsValue - char * : pointer to the current param block. In |
michael@0 | 286 | * the absence of overrides, paramsValue is set to the first |
michael@0 | 287 | * parameter block we find. All subsequent blocks are ignored. |
michael@0 | 288 | * When we find an internal token, the application passed |
michael@0 | 289 | * parameters take precident. |
michael@0 | 290 | */ |
michael@0 | 291 | } else if (PORT_Strncasecmp(line, "parameters", 10) == 0) { |
michael@0 | 292 | /* already have parameters */ |
michael@0 | 293 | if (paramsValue) { |
michael@0 | 294 | continue; |
michael@0 | 295 | } |
michael@0 | 296 | paramsValue = NSSUTIL_Quote(&value[1], '"'); |
michael@0 | 297 | if (paramsValue == NULL) goto loser; |
michael@0 | 298 | continue; |
michael@0 | 299 | } else { |
michael@0 | 300 | /* may need to quote */ |
michael@0 | 301 | char *newLine; |
michael@0 | 302 | if (moduleString) { |
michael@0 | 303 | moduleString = nssutil_DupnCat(moduleString," ", 1); |
michael@0 | 304 | if (moduleString == NULL) goto loser; |
michael@0 | 305 | } |
michael@0 | 306 | moduleString = nssutil_DupnCat(moduleString,line,value-line+1); |
michael@0 | 307 | if (moduleString == NULL) goto loser; |
michael@0 | 308 | newLine = NSSUTIL_Quote(&value[1],'"'); |
michael@0 | 309 | if (newLine == NULL) goto loser; |
michael@0 | 310 | moduleString = nssutil_DupCat(moduleString,newLine); |
michael@0 | 311 | PORT_Free(newLine); |
michael@0 | 312 | if (moduleString == NULL) goto loser; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | /* check to see if it's internal? */ |
michael@0 | 316 | if (PORT_Strncasecmp(line, "NSS=", 4) == 0) { |
michael@0 | 317 | /* This should be case insensitive! reviewers make |
michael@0 | 318 | * me fix it if it's not */ |
michael@0 | 319 | if (PORT_Strstr(line,"internal")) { |
michael@0 | 320 | internal = PR_TRUE; |
michael@0 | 321 | /* override the parameters */ |
michael@0 | 322 | if (paramsValue) { |
michael@0 | 323 | PORT_Free(paramsValue); |
michael@0 | 324 | } |
michael@0 | 325 | paramsValue = NSSUTIL_Quote(params, '"'); |
michael@0 | 326 | } |
michael@0 | 327 | } |
michael@0 | 328 | continue; |
michael@0 | 329 | } |
michael@0 | 330 | if ((moduleString == NULL) || (*moduleString == 0)) { |
michael@0 | 331 | continue; |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | /* |
michael@0 | 335 | * if we are here, we have found a complete stanza. Now write out |
michael@0 | 336 | * any param section we may have found. |
michael@0 | 337 | */ |
michael@0 | 338 | if (paramsValue) { |
michael@0 | 339 | /* we had an override */ |
michael@0 | 340 | if (!skipParams) { |
michael@0 | 341 | moduleString = nssutil_DupnCat(moduleString," parameters=", 12); |
michael@0 | 342 | if (moduleString == NULL) goto loser; |
michael@0 | 343 | moduleString = nssutil_DupCat(moduleString, paramsValue); |
michael@0 | 344 | if (moduleString == NULL) goto loser; |
michael@0 | 345 | } |
michael@0 | 346 | PORT_Free(paramsValue); |
michael@0 | 347 | paramsValue = NULL; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | if ((moduleCount+1) >= useCount) { |
michael@0 | 351 | SECStatus rv; |
michael@0 | 352 | rv = nssutil_growList(&moduleList, &useCount, moduleCount+1); |
michael@0 | 353 | if (rv != SECSuccess) { |
michael@0 | 354 | goto loser; |
michael@0 | 355 | } |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | if (internal) { |
michael@0 | 359 | moduleList[0] = moduleString; |
michael@0 | 360 | } else { |
michael@0 | 361 | moduleList[moduleCount] = moduleString; |
michael@0 | 362 | moduleCount++; |
michael@0 | 363 | } |
michael@0 | 364 | moduleString = NULL; |
michael@0 | 365 | internal = PR_FALSE; |
michael@0 | 366 | skipParams = PR_FALSE; |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | if (moduleString) { |
michael@0 | 370 | PORT_Free(moduleString); |
michael@0 | 371 | moduleString = NULL; |
michael@0 | 372 | } |
michael@0 | 373 | done: |
michael@0 | 374 | /* if we couldn't open a pkcs11 database, look for the old one */ |
michael@0 | 375 | if (fd == NULL) { |
michael@0 | 376 | char *olddbname = _NSSUTIL_GetOldSecmodName(dbname,filename); |
michael@0 | 377 | PRStatus status; |
michael@0 | 378 | |
michael@0 | 379 | /* couldn't get the old name */ |
michael@0 | 380 | if (!olddbname) { |
michael@0 | 381 | goto bail; |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | /* old one exists */ |
michael@0 | 385 | status = PR_Access(olddbname, PR_ACCESS_EXISTS); |
michael@0 | 386 | if (status == PR_SUCCESS) { |
michael@0 | 387 | PR_smprintf_free(olddbname); |
michael@0 | 388 | PORT_ZFree(moduleList, useCount*sizeof(char **)); |
michael@0 | 389 | PORT_SetError(SEC_ERROR_LEGACY_DATABASE); |
michael@0 | 390 | return NULL; |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | bail: |
michael@0 | 394 | if (olddbname) { |
michael@0 | 395 | PR_smprintf_free(olddbname); |
michael@0 | 396 | } |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | return_default: |
michael@0 | 400 | |
michael@0 | 401 | if (!moduleList[0]) { |
michael@0 | 402 | char * newParams; |
michael@0 | 403 | moduleString = PORT_Strdup(NSSUTIL_DEFAULT_INTERNAL_INIT1); |
michael@0 | 404 | newParams = NSSUTIL_Quote(params,'"'); |
michael@0 | 405 | if (newParams == NULL) goto loser; |
michael@0 | 406 | moduleString = nssutil_DupCat(moduleString, newParams); |
michael@0 | 407 | PORT_Free(newParams); |
michael@0 | 408 | if (moduleString == NULL) goto loser; |
michael@0 | 409 | moduleString = nssutil_DupCat(moduleString, |
michael@0 | 410 | NSSUTIL_DEFAULT_INTERNAL_INIT2); |
michael@0 | 411 | if (moduleString == NULL) goto loser; |
michael@0 | 412 | moduleString = nssutil_DupCat(moduleString, |
michael@0 | 413 | NSSUTIL_DEFAULT_SFTKN_FLAGS); |
michael@0 | 414 | if (moduleString == NULL) goto loser; |
michael@0 | 415 | moduleString = nssutil_DupCat(moduleString, |
michael@0 | 416 | NSSUTIL_DEFAULT_INTERNAL_INIT3); |
michael@0 | 417 | if (moduleString == NULL) goto loser; |
michael@0 | 418 | moduleList[0] = moduleString; |
michael@0 | 419 | moduleString = NULL; |
michael@0 | 420 | } |
michael@0 | 421 | failed = PR_FALSE; |
michael@0 | 422 | |
michael@0 | 423 | loser: |
michael@0 | 424 | /* |
michael@0 | 425 | * cleanup |
michael@0 | 426 | */ |
michael@0 | 427 | /* deal with trust cert db here */ |
michael@0 | 428 | if (moduleString) { |
michael@0 | 429 | PORT_Free(moduleString); |
michael@0 | 430 | moduleString = NULL; |
michael@0 | 431 | } |
michael@0 | 432 | if (paramsValue) { |
michael@0 | 433 | PORT_Free(paramsValue); |
michael@0 | 434 | paramsValue = NULL; |
michael@0 | 435 | } |
michael@0 | 436 | if (failed || (moduleList[0] == NULL)) { |
michael@0 | 437 | /* This is wrong! FIXME */ |
michael@0 | 438 | nssutil_releaseSpecList(moduleList); |
michael@0 | 439 | moduleList = NULL; |
michael@0 | 440 | failed = PR_TRUE; |
michael@0 | 441 | } |
michael@0 | 442 | if (fd != NULL) { |
michael@0 | 443 | fclose(fd); |
michael@0 | 444 | } else if (!failed && rw) { |
michael@0 | 445 | /* update our internal module */ |
michael@0 | 446 | nssutil_AddSecmodDBEntry(appName, filename, dbname, moduleList[0], rw); |
michael@0 | 447 | } |
michael@0 | 448 | return moduleList; |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | static SECStatus |
michael@0 | 452 | nssutil_ReleaseSecmodDBData(const char *appName, |
michael@0 | 453 | const char *filename, const char *dbname, |
michael@0 | 454 | char **moduleSpecList, PRBool rw) |
michael@0 | 455 | { |
michael@0 | 456 | if (moduleSpecList) { |
michael@0 | 457 | nssutil_releaseSpecList(moduleSpecList); |
michael@0 | 458 | } |
michael@0 | 459 | return SECSuccess; |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | |
michael@0 | 463 | /* |
michael@0 | 464 | * Delete a module from the Data Base |
michael@0 | 465 | */ |
michael@0 | 466 | static SECStatus |
michael@0 | 467 | nssutil_DeleteSecmodDBEntry(const char *appName, |
michael@0 | 468 | const char *filename, |
michael@0 | 469 | const char *dbname, |
michael@0 | 470 | char *args, |
michael@0 | 471 | PRBool rw) |
michael@0 | 472 | { |
michael@0 | 473 | /* SHDB_FIXME implement */ |
michael@0 | 474 | os_stat_type stat_existing; |
michael@0 | 475 | os_open_permissions_type file_mode; |
michael@0 | 476 | FILE *fd = NULL; |
michael@0 | 477 | FILE *fd2 = NULL; |
michael@0 | 478 | char line[MAX_LINE_LENGTH]; |
michael@0 | 479 | char *dbname2 = NULL; |
michael@0 | 480 | char *block = NULL; |
michael@0 | 481 | char *name = NULL; |
michael@0 | 482 | char *lib = NULL; |
michael@0 | 483 | int name_len, lib_len; |
michael@0 | 484 | PRBool skip = PR_FALSE; |
michael@0 | 485 | PRBool found = PR_FALSE; |
michael@0 | 486 | |
michael@0 | 487 | if (dbname == NULL) { |
michael@0 | 488 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
michael@0 | 489 | return SECFailure; |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | if (!rw) { |
michael@0 | 493 | PORT_SetError(SEC_ERROR_READ_ONLY); |
michael@0 | 494 | return SECFailure; |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | dbname2 = PORT_Strdup(dbname); |
michael@0 | 498 | if (dbname2 == NULL) goto loser; |
michael@0 | 499 | dbname2[strlen(dbname)-1]++; |
michael@0 | 500 | |
michael@0 | 501 | /* get the permissions of the existing file, or use the default */ |
michael@0 | 502 | if (!os_stat(dbname, &stat_existing)) { |
michael@0 | 503 | file_mode = stat_existing.st_mode; |
michael@0 | 504 | } else { |
michael@0 | 505 | file_mode = os_open_permissions_default; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | /* do we really want to use streams here */ |
michael@0 | 509 | fd = fopen(dbname, "r"); |
michael@0 | 510 | if (fd == NULL) goto loser; |
michael@0 | 511 | |
michael@0 | 512 | fd2 = lfopen(dbname2, lfopen_truncate, file_mode); |
michael@0 | 513 | |
michael@0 | 514 | if (fd2 == NULL) goto loser; |
michael@0 | 515 | |
michael@0 | 516 | name = NSSUTIL_ArgGetParamValue("name",args); |
michael@0 | 517 | if (name) { |
michael@0 | 518 | name_len = PORT_Strlen(name); |
michael@0 | 519 | } |
michael@0 | 520 | lib = NSSUTIL_ArgGetParamValue("library",args); |
michael@0 | 521 | if (lib) { |
michael@0 | 522 | lib_len = PORT_Strlen(lib); |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | |
michael@0 | 526 | /* |
michael@0 | 527 | * the following loop takes line separated config files and collapses |
michael@0 | 528 | * the lines to a single string, escaping and quoting as necessary. |
michael@0 | 529 | */ |
michael@0 | 530 | /* loop state variables */ |
michael@0 | 531 | block = NULL; |
michael@0 | 532 | skip = PR_FALSE; |
michael@0 | 533 | while (fgets(line, sizeof(line), fd) != NULL) { |
michael@0 | 534 | /* If we are processing a block (we haven't hit a blank line yet */ |
michael@0 | 535 | if (*line != '\n') { |
michael@0 | 536 | /* skip means we are in the middle of a block we are deleting */ |
michael@0 | 537 | if (skip) { |
michael@0 | 538 | continue; |
michael@0 | 539 | } |
michael@0 | 540 | /* if we haven't found the block yet, check to see if this block |
michael@0 | 541 | * matches our requirements */ |
michael@0 | 542 | if (!found && ((name && (PORT_Strncasecmp(line,"name=",5) == 0) && |
michael@0 | 543 | (PORT_Strncmp(line+5,name,name_len) == 0)) || |
michael@0 | 544 | (lib && (PORT_Strncasecmp(line,"library=",8) == 0) && |
michael@0 | 545 | (PORT_Strncmp(line+8,lib,lib_len) == 0)))) { |
michael@0 | 546 | |
michael@0 | 547 | /* yup, we don't need to save any more data, */ |
michael@0 | 548 | PORT_Free(block); |
michael@0 | 549 | block=NULL; |
michael@0 | 550 | /* we don't need to collect more of this block */ |
michael@0 | 551 | skip = PR_TRUE; |
michael@0 | 552 | /* we don't need to continue searching for the block */ |
michael@0 | 553 | found =PR_TRUE; |
michael@0 | 554 | continue; |
michael@0 | 555 | } |
michael@0 | 556 | /* not our match, continue to collect data in this block */ |
michael@0 | 557 | block = nssutil_DupCat(block,line); |
michael@0 | 558 | continue; |
michael@0 | 559 | } |
michael@0 | 560 | /* we've collected a block of data that wasn't the module we were |
michael@0 | 561 | * looking for, write it out */ |
michael@0 | 562 | if (block) { |
michael@0 | 563 | fwrite(block, PORT_Strlen(block), 1, fd2); |
michael@0 | 564 | PORT_Free(block); |
michael@0 | 565 | block = NULL; |
michael@0 | 566 | } |
michael@0 | 567 | /* If we didn't just delete the this block, keep the blank line */ |
michael@0 | 568 | if (!skip) { |
michael@0 | 569 | fputs(line,fd2); |
michael@0 | 570 | } |
michael@0 | 571 | /* we are definately not in a deleted block anymore */ |
michael@0 | 572 | skip = PR_FALSE; |
michael@0 | 573 | } |
michael@0 | 574 | fclose(fd); |
michael@0 | 575 | fclose(fd2); |
michael@0 | 576 | if (found) { |
michael@0 | 577 | /* rename dbname2 to dbname */ |
michael@0 | 578 | PR_Delete(dbname); |
michael@0 | 579 | PR_Rename(dbname2,dbname); |
michael@0 | 580 | } else { |
michael@0 | 581 | PR_Delete(dbname2); |
michael@0 | 582 | } |
michael@0 | 583 | PORT_Free(dbname2); |
michael@0 | 584 | PORT_Free(lib); |
michael@0 | 585 | PORT_Free(name); |
michael@0 | 586 | PORT_Free(block); |
michael@0 | 587 | return SECSuccess; |
michael@0 | 588 | |
michael@0 | 589 | loser: |
michael@0 | 590 | if (fd != NULL) { |
michael@0 | 591 | fclose(fd); |
michael@0 | 592 | } |
michael@0 | 593 | if (fd2 != NULL) { |
michael@0 | 594 | fclose(fd2); |
michael@0 | 595 | } |
michael@0 | 596 | if (dbname2) { |
michael@0 | 597 | PR_Delete(dbname2); |
michael@0 | 598 | PORT_Free(dbname2); |
michael@0 | 599 | } |
michael@0 | 600 | PORT_Free(lib); |
michael@0 | 601 | PORT_Free(name); |
michael@0 | 602 | return SECFailure; |
michael@0 | 603 | } |
michael@0 | 604 | |
michael@0 | 605 | /* |
michael@0 | 606 | * Add a module to the Data base |
michael@0 | 607 | */ |
michael@0 | 608 | static SECStatus |
michael@0 | 609 | nssutil_AddSecmodDBEntry(const char *appName, |
michael@0 | 610 | const char *filename, const char *dbname, |
michael@0 | 611 | char *module, PRBool rw) |
michael@0 | 612 | { |
michael@0 | 613 | os_stat_type stat_existing; |
michael@0 | 614 | os_open_permissions_type file_mode; |
michael@0 | 615 | FILE *fd = NULL; |
michael@0 | 616 | char *block = NULL; |
michael@0 | 617 | PRBool libFound = PR_FALSE; |
michael@0 | 618 | |
michael@0 | 619 | if (dbname == NULL) { |
michael@0 | 620 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
michael@0 | 621 | return SECFailure; |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | /* can't write to a read only module */ |
michael@0 | 625 | if (!rw) { |
michael@0 | 626 | PORT_SetError(SEC_ERROR_READ_ONLY); |
michael@0 | 627 | return SECFailure; |
michael@0 | 628 | } |
michael@0 | 629 | |
michael@0 | 630 | /* remove the previous version if it exists */ |
michael@0 | 631 | (void) nssutil_DeleteSecmodDBEntry(appName, filename, dbname, module, rw); |
michael@0 | 632 | |
michael@0 | 633 | /* get the permissions of the existing file, or use the default */ |
michael@0 | 634 | if (!os_stat(dbname, &stat_existing)) { |
michael@0 | 635 | file_mode = stat_existing.st_mode; |
michael@0 | 636 | } else { |
michael@0 | 637 | file_mode = os_open_permissions_default; |
michael@0 | 638 | } |
michael@0 | 639 | |
michael@0 | 640 | fd = lfopen(dbname, lfopen_append, file_mode); |
michael@0 | 641 | if (fd == NULL) { |
michael@0 | 642 | return SECFailure; |
michael@0 | 643 | } |
michael@0 | 644 | module = NSSUTIL_ArgStrip(module); |
michael@0 | 645 | while (*module) { |
michael@0 | 646 | int count; |
michael@0 | 647 | char *keyEnd = PORT_Strchr(module,'='); |
michael@0 | 648 | char *value; |
michael@0 | 649 | |
michael@0 | 650 | if (PORT_Strncmp(module, "library=", 8) == 0) { |
michael@0 | 651 | libFound=PR_TRUE; |
michael@0 | 652 | } |
michael@0 | 653 | if (keyEnd == NULL) { |
michael@0 | 654 | block = nssutil_DupCat(block, module); |
michael@0 | 655 | break; |
michael@0 | 656 | } |
michael@0 | 657 | block = nssutil_DupnCat(block, module, keyEnd-module+1); |
michael@0 | 658 | if (block == NULL) { goto loser; } |
michael@0 | 659 | value = NSSUTIL_ArgFetchValue(&keyEnd[1], &count); |
michael@0 | 660 | if (value) { |
michael@0 | 661 | block = nssutil_DupCat(block, NSSUTIL_ArgStrip(value)); |
michael@0 | 662 | PORT_Free(value); |
michael@0 | 663 | } |
michael@0 | 664 | if (block == NULL) { goto loser; } |
michael@0 | 665 | block = nssutil_DupnCat(block, "\n", 1); |
michael@0 | 666 | module = keyEnd + 1 + count; |
michael@0 | 667 | module = NSSUTIL_ArgStrip(module); |
michael@0 | 668 | } |
michael@0 | 669 | if (block) { |
michael@0 | 670 | if (!libFound) { |
michael@0 | 671 | fprintf(fd,"library=\n"); |
michael@0 | 672 | } |
michael@0 | 673 | fwrite(block, PORT_Strlen(block), 1, fd); |
michael@0 | 674 | fprintf(fd,"\n"); |
michael@0 | 675 | PORT_Free(block); |
michael@0 | 676 | block = NULL; |
michael@0 | 677 | } |
michael@0 | 678 | fclose(fd); |
michael@0 | 679 | return SECSuccess; |
michael@0 | 680 | |
michael@0 | 681 | loser: |
michael@0 | 682 | PORT_Free(block); |
michael@0 | 683 | fclose(fd); |
michael@0 | 684 | return SECFailure; |
michael@0 | 685 | } |
michael@0 | 686 | |
michael@0 | 687 | |
michael@0 | 688 | char ** |
michael@0 | 689 | NSSUTIL_DoModuleDBFunction(unsigned long function,char *parameters, void *args) |
michael@0 | 690 | { |
michael@0 | 691 | char *secmod = NULL; |
michael@0 | 692 | char *appName = NULL; |
michael@0 | 693 | char *filename = NULL; |
michael@0 | 694 | NSSDBType dbType = NSS_DB_TYPE_NONE; |
michael@0 | 695 | PRBool rw; |
michael@0 | 696 | static char *success="Success"; |
michael@0 | 697 | char **rvstr = NULL; |
michael@0 | 698 | |
michael@0 | 699 | |
michael@0 | 700 | secmod = _NSSUTIL_GetSecmodName(parameters, &dbType, &appName, |
michael@0 | 701 | &filename, &rw); |
michael@0 | 702 | if ((dbType == NSS_DB_TYPE_LEGACY) || |
michael@0 | 703 | (dbType == NSS_DB_TYPE_MULTIACCESS)) { |
michael@0 | 704 | /* we can't handle the old database, only softoken can */ |
michael@0 | 705 | PORT_SetError(SEC_ERROR_LEGACY_DATABASE); |
michael@0 | 706 | rvstr = NULL; |
michael@0 | 707 | goto done; |
michael@0 | 708 | } |
michael@0 | 709 | |
michael@0 | 710 | switch (function) { |
michael@0 | 711 | case SECMOD_MODULE_DB_FUNCTION_FIND: |
michael@0 | 712 | rvstr = nssutil_ReadSecmodDB(appName,filename, |
michael@0 | 713 | secmod,(char *)parameters,rw); |
michael@0 | 714 | break; |
michael@0 | 715 | case SECMOD_MODULE_DB_FUNCTION_ADD: |
michael@0 | 716 | rvstr = (nssutil_AddSecmodDBEntry(appName, filename, |
michael@0 | 717 | secmod, (char *)args, rw) |
michael@0 | 718 | == SECSuccess) ? &success: NULL; |
michael@0 | 719 | break; |
michael@0 | 720 | case SECMOD_MODULE_DB_FUNCTION_DEL: |
michael@0 | 721 | rvstr = (nssutil_DeleteSecmodDBEntry(appName, filename, |
michael@0 | 722 | secmod, (char *)args, rw) |
michael@0 | 723 | == SECSuccess) ? &success: NULL; |
michael@0 | 724 | break; |
michael@0 | 725 | case SECMOD_MODULE_DB_FUNCTION_RELEASE: |
michael@0 | 726 | rvstr = (nssutil_ReleaseSecmodDBData(appName, filename, |
michael@0 | 727 | secmod, (char **)args, rw) |
michael@0 | 728 | == SECSuccess) ? &success: NULL; |
michael@0 | 729 | break; |
michael@0 | 730 | } |
michael@0 | 731 | done: |
michael@0 | 732 | if (secmod) PR_smprintf_free(secmod); |
michael@0 | 733 | if (appName) PORT_Free(appName); |
michael@0 | 734 | if (filename) PORT_Free(filename); |
michael@0 | 735 | return rvstr; |
michael@0 | 736 | } |