Bug Summary

File:db/file.c
Warning:line 612, column 7
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name file.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/builds/gsasl/shishi/db -resource-dir /usr/lib64/clang/14.0.0 -D HAVE_CONFIG_H -I . -I .. -I ../lib/gl -I ../lib/gl -D LOCALEDIR="/usr/local/share/locale" -D SYSTEMCFGFILE="/usr/local/etc/shishi/shisa.conf" -D DEFAULTDBPATH="/usr/local/var/shishi" -D PIC -internal-isystem /usr/lib64/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/12/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/builds/gsasl/shishi/db -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/gsasl/shishi/clang-analyzer/2022-08-08-065803-50050-1 -x c file.c
1/* file.c --- File based Shisa database.
2 * Copyright (C) 2002-2022 Simon Josefsson
3 *
4 * This file is part of Shishi.
5 *
6 * Shishi is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Shishi is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, see http://www.gnu.org/licenses or write
18 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
19 * Floor, Boston, MA 02110-1301, USA
20 *
21 */
22
23/*
24 * Theory of operation:
25 *
26 * Data is stored in the standard file system, so it is subject to
27 * normal access permission infrastructure, e.g. POSIX ACL or normal
28 * Unix file permissions. A definition of the file database looks
29 * like:
30 *
31 * file LOCATION OPTIONS
32 *
33 * Where LOCATION is a path name, e.g. /var/shisa. No OPTIONS are
34 * currently implemented.
35 *
36 * Realms are directories in LOCATION. Principals are directories in
37 * realm directories. Characters outside A-Za-z0-9_- are escaped
38 * using the URL encoding, e.g. example/host%2fwww denote the
39 * "host/www" principal in the "example" realm.
40 *
41 * Example file tree:
42 *
43 * LOCATION/EXAMPLE.ORG
44 * LOCATION/EXAMPLE.ORG/krbtgt%2fEXAMPLE.ORG
45 * LOCATION/EXAMPLE.ORG/host%2fwww.example.org
46 * LOCATION/EXAMPLE.NET
47 * LOCATION/EXAMPLE.NET/krbtgt%2fEXAMPLE.NET
48 *
49 */
50
51/* XXX fix race conditions. */
52
53#include "info.h"
54
55/* Get prototypes. */
56#include "file.h"
57
58/* Get low-level file utilities. */
59#include "fileutil.h"
60
61struct Shisa_file
62{
63 char *path;
64 int readonly;
65 int allowcreate;
66};
67typedef struct Shisa_file Shisa_file;
68
69enum
70{
71 READ_ONLY_OPTION = 0,
72 ALLOW_CREATE_OPTION = 1,
73 THE_END
74};
75
76static const char *const _shisa_file_opts[] = {
77 /* [READ_ONLY_OPTION] = */ "read-only",
78 /* [ALLOW_CREATE_OPTION] = */ "allow-create",
79 /* [THE_END] = */ NULL((void*)0)
80};
81
82static int
83shisa_file_cfg (Shisa * dbh, Shisa_file * info, const char *option)
84{
85 char *opt = option ? xstrdup (option) : NULL((void*)0);
86 char *p = opt;
87 char *value;
88 int res;
89
90 while (p != NULL((void*)0) && *p != '\0')
91 {
92 switch (getsubopt (&p, (char *const *) _shisa_file_opts, &value))
93 {
94 case READ_ONLY_OPTION:
95 info->readonly = 1;
96 break;
97
98 case ALLOW_CREATE_OPTION:
99 info->allowcreate = 1;
100 break;
101
102 default:
103 shisa_info (dbh, "Unknown file database option: `%s'.", value);
104 res = SHISA_CFG_SYNTAX_ERROR;
105 goto out;
106 break;
107 }
108 }
109
110 res = SHISA_OK;
111
112out:
113 free (opt);
114 return res;
115}
116
117/* Initialize file backend, i.e., parse options and check if file root
118 exists and allocate backend handle. */
119int
120shisa_file_init (Shisa * dbh,
121 const char *location, const char *options, void **state)
122{
123 Shisa_file *info;
124 int rc;
125
126 if (!_shisa_isdir (location))
127 return SHISA_OPEN_ERROR;
128
129 *state = info = xcalloc (1, sizeof (*info));
130
131 rc = shisa_file_cfg (dbh, info, options);
132 if (rc != SHISA_OK)
133 return rc;
134
135 info->path = xstrdup (location);
136
137 return SHISA_OK;
138}
139
140/* Destroy backend handle. */
141void
142shisa_file_done (Shisa * dbh, void *state)
143{
144 Shisa_file *info = state;
145
146 if (info)
147 free (info->path);
148 free (info);
149}
150
151/* Return a list of all realm names in backend, as zero-terminated
152 UTF-8 strings. The caller must deallocate the strings. */
153int
154shisa_file_enumerate_realms (Shisa * dbh,
155 void *state, char ***realms, size_t *nrealms)
156{
157 Shisa_file *info = state;
158
159 if (_shisa_lsdir (info->path, realms, nrealms) != 0)
160 return SHISA_ENUMERATE_REALM_ERROR;
161
162 return SHISA_OK;
163}
164
165/* Return a list of all principals in realm in backend, as
166 zero-terminated UTF-8 strings. The caller must deallocate the
167 strings. */
168int
169shisa_file_enumerate_principals (Shisa * dbh,
170 void *state,
171 const char *realm,
172 char ***principals, size_t *nprincipals)
173{
174 Shisa_file *info = state;
175
176 if (!_shisa_isdir2 (info->path, realm))
177 return SHISA_NO_REALM;
178
179 if (_shisa_lsdir2 (info->path, realm, principals, nprincipals) != 0)
180 return SHISA_ENUMERATE_PRINCIPAL_ERROR;
181
182 return SHISA_OK;
183}
184
185/* Return information about specified PRINCIPAL@REALM. Can also be
186 used check existence of principal entry, with a NULL PH. */
187int
188shisa_file_principal_find (Shisa * dbh,
189 void *state,
190 const char *realm,
191 const char *principal, Shisa_principal * ph)
192{
193 Shisa_file *info = state;
194
195 if (!_shisa_isdir3 (info->path, realm, principal))
196 return SHISA_NO_PRINCIPAL;
197
198 if (!ph)
199 return SHISA_OK;
200
201 ph->notusedbefore =
202 _shisa_mtime4 (info->path, realm, principal, "validfrom.stamp");
203 ph->isdisabled =
204 _shisa_isfile4 (info->path, realm, principal, "disabled.flag");
205 ph->kvno = _shisa_uint32link4 (info->path, realm, principal, "latest.key");
206 ph->lastinitialtgt =
207 _shisa_mtime4 (info->path, realm, principal, "lastinitaltgt.stamp");
208 ph->lastinitialrequest =
209 _shisa_mtime4 (info->path, realm, principal, "lastinitial.stamp");
210 ph->lasttgt = _shisa_mtime4 (info->path, realm, principal, "lasttgt.stamp");
211 ph->lastrenewal =
212 _shisa_mtime4 (info->path, realm, principal, "lastrenewal.stamp");
213 ph->passwordexpire =
214 _shisa_mtime4 (info->path, realm, principal, "passwordexpire.stamp");
215 ph->accountexpire =
216 _shisa_mtime4 (info->path, realm, principal, "accountexpire.stamp");
217
218 return SHISA_OK;
219}
220
221static int
222realm_add (Shisa * dbh, void *state, const char *realm)
223{
224 Shisa_file *info = state;
225
226 if (_shisa_isdir2 (info->path, realm))
227 return SHISA_ADD_REALM_EXISTS;
228
229 if (_shisa_mkdir2 (info->path, realm) != 0)
230 return SHISA_ADD_REALM_ERROR;
231
232 return SHISA_OK;
233
234}
235
236static int
237principal_add (Shisa * dbh,
238 void *state,
239 const char *realm,
240 const char *principal,
241 const Shisa_principal * ph, const Shisa_key * key)
242{
243 Shisa_file *info = state;
244
245 if (!_shisa_isdir2 (info->path, realm))
4
Assuming the condition is false
5
Taking false branch
246 return SHISA_NO_REALM;
247
248 if (_shisa_isdir3 (info->path, realm, principal))
6
Assuming the condition is false
7
Taking false branch
249 return SHISA_ADD_PRINCIPAL_EXISTS;
250
251 if (_shisa_mkdir3 (info->path, realm, principal) != 0)
8
Assuming the condition is false
9
Taking false branch
252 return SHISA_ADD_PRINCIPAL_ERROR;
253
254 if (ph)
10
Assuming 'ph' is null
11
Taking false branch
255 shisa_file_principal_update (dbh, state, realm, principal, ph);
256
257 if (key)
12
Assuming 'key' is non-null
13
Taking true branch
258 shisa_file_key_add (dbh, state, realm, principal, key);
14
Calling 'shisa_file_key_add'
259
260 return SHISA_OK;
261}
262
263/* Add new PRINCIPAL@REALM with specified information and key. If
264 PRINCIPAL is NULL, then add realm REALM. */
265int
266shisa_file_principal_add (Shisa * dbh,
267 void *state,
268 const char *realm,
269 const char *principal,
270 const Shisa_principal * ph, const Shisa_key * key)
271{
272 int rc;
273
274 if (principal == NULL((void*)0))
1
Assuming 'principal' is not equal to NULL
2
Taking false branch
275 rc = realm_add (dbh, state, realm);
276 else
277 rc = principal_add (dbh, state, realm, principal, ph, key);
3
Calling 'principal_add'
278
279 return rc;
280}
281
282/* Modify information for specified PRINCIPAL@REALM. */
283int
284shisa_file_principal_update (Shisa * dbh,
285 void *state,
286 const char *realm,
287 const char *principal,
288 const Shisa_principal * ph)
289{
290 return SHISA_OK;
291}
292
293static int
294realm_remove (Shisa * dbh, void *state, const char *realm)
295{
296 Shisa_file *info = state;
297 size_t nprincipals = 0;
298 int rc;
299
300 if (!_shisa_isdir2 (info->path, realm))
301 return SHISA_NO_REALM;
302
303 rc =
304 shisa_file_enumerate_principals (dbh, state, realm, NULL((void*)0), &nprincipals);
305 if (rc != SHISA_OK)
306 return rc;
307
308 if (nprincipals > 0)
309 return SHISA_REMOVE_REALM_NONEMPTY;
310
311 if (_shisa_rmdir2 (info->path, realm) != 0)
312 return SHISA_REMOVE_REALM_ERROR;
313
314 return SHISA_OK;
315}
316
317static int
318remove_keys (Shisa * dbh,
319 void *state, const char *realm, const char *principal)
320{
321 Shisa_file *info = state;
322 char **files;
323 size_t nfiles;
324 size_t i;
325 int rc;
326
327 files = NULL((void*)0);
328 nfiles = 0;
329
330 rc = _shisa_ls4 (info->path, realm, principal, "keys", &files, &nfiles);
331 if (rc != SHISA_OK)
332 return rc;
333
334 for (i = 0; i < nfiles; i++)
335 {
336 rc = _shisa_rm5 (info->path, realm, principal, "keys", files[i]);
337 free (files[i]);
338 }
339 free (files);
340
341 rc = _shisa_rmdir4 (info->path, realm, principal, "keys");
342 if (rc != SHISA_OK)
343 return rc;
344
345 return SHISA_OK;
346}
347
348#if 0
349static int
350remove_info (Shisa * dbh,
351 void *state, const char *realm, const char *principal)
352{
353 Shisa_file *info = state;
354 char **files;
355 size_t nfiles;
356 size_t i;
357 int rc;
358
359 files = NULL((void*)0);
360 nfiles = 0;
361
362 rc = _shisa_ls3 (info->path, realm, principal, &files, &nfiles);
363 if (rc != SHISA_OK)
364 return rc;
365
366 for (i = 0; i < nfiles; i++)
367 {
368 rc = _shisa_rm4 (info->path, realm, principal, files[i]);
369 free (files[i]);
370 }
371 free (files);
372
373 return SHISA_OK;
374}
375#endif
376
377static int
378principal_remove (Shisa * dbh,
379 void *state, const char *realm, const char *principal)
380{
381 Shisa_file *info = state;
382 int rc;
383
384 if (!_shisa_isdir2 (info->path, realm))
385 return SHISA_NO_REALM;
386
387 if (!_shisa_isdir3 (info->path, realm, principal))
388 return SHISA_NO_PRINCIPAL;
389
390 rc = remove_keys (dbh, state, realm, principal);
391 if (rc != SHISA_OK)
392 return rc;
393
394 if (_shisa_rmdir3 (info->path, realm, principal) != 0)
395 return SHISA_REMOVE_PRINCIPAL_ERROR;
396
397 return SHISA_OK;
398}
399
400/* Remove PRINCIPAL@REALM, or REALM if PRINCIPAL is NULL. Realms must
401 be empty for them to be successfully removed. */
402int
403shisa_file_principal_remove (Shisa * dbh,
404 void *state,
405 const char *realm, const char *principal)
406{
407 int rc;
408
409 if (principal == NULL((void*)0))
410 rc = realm_remove (dbh, state, realm);
411 else
412 rc = principal_remove (dbh, state, realm, principal);
413
414 return rc;
415}
416
417static int
418read_key (Shisa * dbh,
419 Shisa_file * info,
420 const char *realm,
421 const char *principal, const char *keyfile, Shisa_key ** key)
422{
423 Shisa_key tmpkey;
424 FILE *fh;
425 char *file;
426 size_t passwdlen;
427 char junk;
428 int rc;
429
430 asprintf (&file, "keys/%s", keyfile);
431 fh = _shisa_fopen4 (info->path, realm, principal, file, "r");
432 free (file);
433 if (!fh)
434 return SHISA_NO_KEY;
435
436 memset (&tmpkey, 0, sizeof (tmpkey));
437
438 rc =
439 fscanf (fh, "%" PRIi32"i" " %zu %zu %zu %zu %d", &tmpkey.etype,
440 &tmpkey.keylen, &tmpkey.saltlen, &tmpkey.str2keyparamlen,
441 &passwdlen, &tmpkey.priority);
442 if (rc != 5 && rc != 6)
443 return SHISA_NO_KEY;
444
445 if (rc == 5)
446 tmpkey.priority = 0;
447
448 if (rc == 6)
449 /* We can't include '\n' in scanf format above, because any
450 whitespace on the next line will be skipped. */
451 if (fread (&junk, 1, 1, fh) != 1 || junk != '\n')
452 return SHISA_NO_KEY;
453
454 if (tmpkey.keylen > 0)
455 {
456 tmpkey.key = xmalloc (tmpkey.keylen + 1);
457 if (fread (tmpkey.key, 1, tmpkey.keylen, fh) != tmpkey.keylen)
458 return SHISA_NO_KEY;
459 tmpkey.key[tmpkey.keylen] = '\0';
460 }
461
462 if (tmpkey.saltlen > 0)
463 {
464 tmpkey.salt = xmalloc (tmpkey.saltlen + 1);
465 if (fread (tmpkey.salt, 1, tmpkey.saltlen, fh) != tmpkey.saltlen)
466 return SHISA_NO_KEY;
467 tmpkey.salt[tmpkey.saltlen] = '\0';
468 }
469
470 if (tmpkey.str2keyparamlen > 0)
471 {
472 tmpkey.str2keyparam = xmalloc (tmpkey.str2keyparamlen + 1);
473 if (fread (tmpkey.str2keyparam, 1, tmpkey.str2keyparamlen, fh) !=
474 tmpkey.str2keyparamlen)
475 return SHISA_NO_KEY;
476 tmpkey.str2keyparam[tmpkey.str2keyparamlen] = '\0';
477 }
478
479 if (passwdlen > 0)
480 {
481 tmpkey.password = xmalloc (passwdlen + 1);
482 if (fread (tmpkey.password, 1, passwdlen, fh) != passwdlen)
483 return SHISA_NO_KEY;
484 tmpkey.password[passwdlen] = '\0';
485 }
486
487 rc = fclose (fh);
488 if (rc != 0)
489 {
490 perror (keyfile);
491 return SHISA_NO_KEY;
492 }
493
494 *key = xmalloc (sizeof (**key));
495 memcpy (*key, &tmpkey, sizeof (tmpkey));
496
497 return SHISA_OK;
498}
499
500static int
501key_match (const Shisa_key * hint, Shisa_key * key)
502{
503 int ok = 1;
504
505 if (hint->kvno)
506 ok = ok && hint->kvno == key->kvno;
507 if (hint->etype)
508 ok = ok && hint->etype == key->etype;
509 if (hint->keylen)
510 ok = ok && hint->keylen == key->keylen &&
511 memcmp (hint->key, key->key, key->keylen) == 0;
512 if (hint->saltlen)
513 ok = ok && hint->saltlen == key->saltlen &&
514 memcmp (hint->salt, key->salt, key->saltlen) == 0;
515 if (hint->str2keyparamlen)
516 ok = ok && hint->str2keyparamlen == key->str2keyparamlen &&
517 memcmp (hint->str2keyparam, key->str2keyparam,
518 key->str2keyparamlen) == 0;
519 if (hint->password)
520 ok = ok && strcmp (hint->password, key->password) == 0;
521
522 return ok;
523}
524
525/* Get all keys matching HINT for specified PRINCIPAL@REALM. The
526 caller must deallocate the returned keys. If HINT is NULL, then
527 all keys are returned. */
528int
529shisa_file_keys_find (Shisa * dbh,
530 void *state,
531 const char *realm,
532 const char *principal,
533 const Shisa_key * hint,
534 Shisa_key *** keys, size_t *nkeys)
535{
536 Shisa_file *info = state;
537 Shisa_key *tmpkey;
538 char **files;
539 size_t nfiles, matched = 0;
540 size_t i;
541 int rc;
542
543 files = NULL((void*)0);
544 nfiles = 0;
545
546 rc = _shisa_ls4 (info->path, realm, principal, "keys", &files, &nfiles);
547 if (rc != SHISA_OK)
548 return SHISA_ENUMERATE_KEY_ERROR;
549
550 if (nkeys)
551 *nkeys = nfiles;
552 if (keys)
553 *keys = xmalloc (nfiles * sizeof (**keys));
554 for (i = 0; i < nfiles; i++)
555 {
556 if (rc == SHISA_OK &&
557 (rc = read_key (dbh, info, realm, principal,
558 files[i], &tmpkey)) == SHISA_OK)
559 {
560 if (hint == NULL((void*)0) || key_match (hint, tmpkey))
561 {
562 if (keys)
563 (*keys)[matched] = tmpkey;
564 matched++;
565 }
566 else
567 shisa_key_free (dbh, tmpkey);
568 }
569 free (files[i]);
570 }
571
572 if (nfiles > 0)
573 free (files);
574
575 if (nkeys)
576 *nkeys = matched;
577
578 return rc;
579}
580
581/* Add key for PRINCIPAL@REALM. */
582int
583shisa_file_key_add (Shisa * dbh,
584 void *state,
585 const char *realm,
586 const char *principal, const Shisa_key * key)
587{
588 Shisa_file *info = state;
589 size_t passwdlen = key
14.1
'key' is non-null
&& key->password ? strlen (key->password) : 0;
15
Assuming field 'password' is null
16
'?' condition is false
590 char *file = NULL((void*)0);
591 size_t num = 0;
592 FILE *fh;
593
594 if (!key
16.1
'key' is non-null
)
595 return SHISA_NO_KEY;
596
597 if (!_shisa_isdir4 (info->path, realm, principal, "keys") &&
17
Assuming the condition is false
598 _shisa_mkdir4 (info->path, realm, principal, "keys"))
599 return SHISA_NO_KEY;
600
601 do
18
Loop condition is false. Exiting loop
602 {
603 free (file);
604 asprintf (&file, "keys/%" PRIu32"u" "-%d-%lu.key", key->kvno, key->etype,
605 num++);
606 }
607 while (_shisa_isfile4 (info->path, realm, principal, file));
608 fh = _shisa_fopen4 (info->path, realm, principal, file, "w");
609 free (file);
19
Memory is released
610 if (!fh)
20
Assuming 'fh' is null
21
Taking true branch
611 {
612 perror (file);
22
Use of memory after it is freed
613 return SHISA_ADD_KEY_ERROR;
614 }
615
616 fprintf (fh, "%" PRIi32"i" " %zu %zu %zu %zu %d\n", key->etype, key->keylen,
617 key->saltlen, key->str2keyparamlen, passwdlen, key->priority);
618 if (key->keylen > 0)
619 fwrite (key->key, 1, key->keylen, fh);
620 if (key->saltlen > 0)
621 fwrite (key->salt, 1, key->saltlen, fh);
622 if (key->str2keyparamlen > 0)
623 fwrite (key->str2keyparam, 1, key->str2keyparamlen, fh);
624 if (passwdlen > 0)
625 fwrite (key->password, 1, passwdlen, fh);
626
627 fclose (fh);
628
629 return SHISA_OK;
630}
631
632/* Update a key for PRINCIPAL@REALM. The OLDKEY must uniquely
633 determine the key to update, i.e., shishi_keys_find using OLDKEY as
634 HINT must return exactly 1 key. */
635int
636shisa_file_key_update (Shisa * dbh,
637 void *state,
638 const char *realm,
639 const char *principal,
640 const Shisa_key * oldkey, const Shisa_key * newkey)
641{
642 return SHISA_NO_KEY;
643}
644
645/* Remove a key for PRINCIPAL@REALM. The KEY must uniquely determine
646 the key to remove, i.e., shishi_keys_find using KEY as HINT must
647 return exactly 1 key. */
648int
649shisa_file_key_remove (Shisa * dbh,
650 void *state,
651 const char *realm,
652 const char *principal, const Shisa_key * key)
653{
654 Shisa_file *info = state;
655 Shisa_key *tmpkey;
656 char **files;
657 size_t nfiles;
658 size_t i;
659 int rc;
660 char *found = NULL((void*)0);
661
662 files = NULL((void*)0);
663 nfiles = 0;
664
665 rc = _shisa_ls4 (info->path, realm, principal, "keys", &files, &nfiles);
666 if (rc != SHISA_OK)
667 return rc;
668
669 for (i = 0; i < nfiles; i++)
670 {
671 if (rc == SHISA_OK &&
672 (rc = read_key (dbh, info, realm, principal,
673 files[i], &tmpkey)) == SHISA_OK)
674 {
675 if (key == NULL((void*)0) || key_match (key, tmpkey))
676 {
677 if (found)
678 {
679 free (found);
680 rc = SHISA_MULTIPLE_KEY_MATCH;
681 }
682 else
683 found = xstrdup (files[i]);
684 }
685 shisa_key_free (dbh, tmpkey);
686 }
687 free (files[i]);
688 }
689
690 if (nfiles > 0)
691 free (files);
692
693 if (rc != SHISA_OK)
694 return rc;
695
696 if (!found)
697 return SHISA_NO_KEY;
698
699 rc = _shisa_rm5 (info->path, realm, principal, "keys", found);
700 free (found);
701 return rc;
702}