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