comparison nss/lib/util/utilmod.c @ 0:1e5118fa0cb1

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