Annotation of src/usr.bin/ssh/authfile.c, Revision 1.117
1.117 ! tim 1: /* $OpenBSD: authfile.c,v 1.116 2015/07/09 09:49:46 markus Exp $ */
1.1 deraadt 2: /*
1.99 markus 3: * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
1.19 deraadt 4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.9 deraadt 24: */
1.1 deraadt 25:
1.62 stevesk 26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
1.76 deraadt 29: #include <sys/uio.h>
1.1 deraadt 30:
1.69 stevesk 31: #include <errno.h>
1.68 stevesk 32: #include <fcntl.h>
1.74 stevesk 33: #include <stdio.h>
1.73 stevesk 34: #include <stdlib.h>
1.71 stevesk 35: #include <string.h>
1.70 stevesk 36: #include <unistd.h>
1.110 deraadt 37: #include <limits.h>
1.15 markus 38:
1.25 markus 39: #include "cipher.h"
1.1 deraadt 40: #include "ssh.h"
1.25 markus 41: #include "log.h"
1.27 itojun 42: #include "authfile.h"
1.44 markus 43: #include "rsa.h"
1.60 dtucker 44: #include "misc.h"
1.61 djm 45: #include "atomicio.h"
1.116 markus 46: #include "sshkey.h"
1.107 djm 47: #include "sshbuf.h"
48: #include "ssherr.h"
1.108 djm 49: #include "krl.h"
1.1 deraadt 50:
1.88 djm 51: #define MAX_KEY_FILE_SIZE (1024 * 1024)
52:
1.86 djm 53: /* Save a key blob to a file */
54: static int
1.107 djm 55: sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
1.86 djm 56: {
1.107 djm 57: int fd, oerrno;
1.86 djm 58:
1.107 djm 59: if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
60: return SSH_ERR_SYSTEM_ERROR;
61: if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf),
62: sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
63: oerrno = errno;
1.86 djm 64: close(fd);
65: unlink(filename);
1.107 djm 66: errno = oerrno;
67: return SSH_ERR_SYSTEM_ERROR;
1.86 djm 68: }
69: close(fd);
1.107 djm 70: return 0;
1.86 djm 71: }
72:
73: int
1.107 djm 74: sshkey_save_private(struct sshkey *key, const char *filename,
75: const char *passphrase, const char *comment,
76: int force_new_format, const char *new_format_cipher, int new_format_rounds)
1.86 djm 77: {
1.107 djm 78: struct sshbuf *keyblob = NULL;
79: int r;
1.86 djm 80:
1.107 djm 81: if ((keyblob = sshbuf_new()) == NULL)
82: return SSH_ERR_ALLOC_FAIL;
83: if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
84: force_new_format, new_format_cipher, new_format_rounds)) != 0)
1.86 djm 85: goto out;
1.107 djm 86: if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
1.86 djm 87: goto out;
1.107 djm 88: r = 0;
1.86 djm 89: out:
1.107 djm 90: sshbuf_free(keyblob);
91: return r;
1.86 djm 92: }
93:
1.88 djm 94: /* Load a key from a fd into a buffer */
95: int
1.109 djm 96: sshkey_load_file(int fd, struct sshbuf *blob)
1.86 djm 97: {
1.88 djm 98: u_char buf[1024];
1.86 djm 99: size_t len;
1.51 fgsch 100: struct stat st;
1.107 djm 101: int r;
1.8 markus 102:
1.107 djm 103: if (fstat(fd, &st) < 0)
104: return SSH_ERR_SYSTEM_ERROR;
1.88 djm 105: if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
1.107 djm 106: st.st_size > MAX_KEY_FILE_SIZE)
107: return SSH_ERR_INVALID_FORMAT;
1.88 djm 108: for (;;) {
109: if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
110: if (errno == EPIPE)
111: break;
1.107 djm 112: r = SSH_ERR_SYSTEM_ERROR;
113: goto out;
1.88 djm 114: }
1.107 djm 115: if ((r = sshbuf_put(blob, buf, len)) != 0)
116: goto out;
117: if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
118: r = SSH_ERR_INVALID_FORMAT;
119: goto out;
1.88 djm 120: }
121: }
122: if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
1.107 djm 123: st.st_size != (off_t)sshbuf_len(blob)) {
124: r = SSH_ERR_FILE_CHANGED;
125: goto out;
1.8 markus 126: }
1.107 djm 127: r = 0;
1.88 djm 128:
1.107 djm 129: out:
130: explicit_bzero(buf, sizeof(buf));
131: if (r != 0)
132: sshbuf_reset(blob);
133: return r;
1.86 djm 134: }
1.8 markus 135:
1.106 markus 136: #ifdef WITH_SSH1
1.86 djm 137: /*
138: * Loads the public part of the ssh v1 key file. Returns NULL if an error was
139: * encountered (the file does not exist or is not readable), and the key
140: * otherwise.
141: */
1.107 djm 142: static int
1.109 djm 143: sshkey_load_public_rsa1(int fd, struct sshkey **keyp, char **commentp)
1.86 djm 144: {
1.107 djm 145: struct sshbuf *b = NULL;
146: int r;
1.86 djm 147:
1.107 djm 148: *keyp = NULL;
149: if (commentp != NULL)
150: *commentp = NULL;
1.8 markus 151:
1.107 djm 152: if ((b = sshbuf_new()) == NULL)
153: return SSH_ERR_ALLOC_FAIL;
1.109 djm 154: if ((r = sshkey_load_file(fd, b)) != 0)
1.107 djm 155: goto out;
156: if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0)
157: goto out;
158: r = 0;
159: out:
160: sshbuf_free(b);
161: return r;
1.1 deraadt 162: }
1.107 djm 163: #endif /* WITH_SSH1 */
1.1 deraadt 164:
1.107 djm 165: /* XXX remove error() calls from here? */
1.63 dtucker 166: int
1.107 djm 167: sshkey_perm_ok(int fd, const char *filename)
1.15 markus 168: {
169: struct stat st;
170:
1.38 markus 171: if (fstat(fd, &st) < 0)
1.107 djm 172: return SSH_ERR_SYSTEM_ERROR;
1.38 markus 173: /*
174: * if a key owned by the user is accessed, then we check the
175: * permissions of the file. if the key owned by a different user,
176: * then we don't care.
177: */
178: if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
1.15 markus 179: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
180: error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
181: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1.38 markus 182: error("Permissions 0%3.3o for '%s' are too open.",
1.54 djm 183: (u_int)st.st_mode & 0777, filename);
1.114 djm 184: error("It is required that your private key files are NOT accessible by others.");
1.29 markus 185: error("This private key will be ignored.");
1.107 djm 186: return SSH_ERR_KEY_BAD_PERMISSIONS;
1.15 markus 187: }
1.107 djm 188: return 0;
1.29 markus 189: }
190:
1.107 djm 191: /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
192: int
193: sshkey_load_private_type(int type, const char *filename, const char *passphrase,
194: struct sshkey **keyp, char **commentp, int *perm_ok)
1.86 djm 195: {
1.107 djm 196: int fd, r;
1.99 markus 197:
1.107 djm 198: *keyp = NULL;
199: if (commentp != NULL)
200: *commentp = NULL;
1.86 djm 201:
1.107 djm 202: if ((fd = open(filename, O_RDONLY)) < 0) {
1.78 dtucker 203: if (perm_ok != NULL)
204: *perm_ok = 0;
1.107 djm 205: return SSH_ERR_SYSTEM_ERROR;
1.78 dtucker 206: }
1.107 djm 207: if (sshkey_perm_ok(fd, filename) != 0) {
1.67 dtucker 208: if (perm_ok != NULL)
209: *perm_ok = 0;
1.107 djm 210: r = SSH_ERR_KEY_BAD_PERMISSIONS;
211: goto out;
1.29 markus 212: }
1.67 dtucker 213: if (perm_ok != NULL)
214: *perm_ok = 1;
1.86 djm 215:
1.109 djm 216: r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
217: out:
218: close(fd);
219: return r;
220: }
221:
222: int
223: sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
224: struct sshkey **keyp, char **commentp)
225: {
226: struct sshbuf *buffer = NULL;
227: int r;
228:
1.107 djm 229: if ((buffer = sshbuf_new()) == NULL) {
230: r = SSH_ERR_ALLOC_FAIL;
231: goto out;
1.15 markus 232: }
1.109 djm 233: if ((r = sshkey_load_file(fd, buffer)) != 0 ||
234: (r = sshkey_parse_private_fileblob_type(buffer, type,
235: passphrase, keyp, commentp)) != 0)
1.107 djm 236: goto out;
1.109 djm 237:
238: /* success */
1.107 djm 239: r = 0;
240: out:
241: if (buffer != NULL)
242: sshbuf_free(buffer);
243: return r;
1.29 markus 244: }
245:
1.107 djm 246: /* XXX this is almost identical to sshkey_load_private_type() */
247: int
248: sshkey_load_private(const char *filename, const char *passphrase,
249: struct sshkey **keyp, char **commentp)
1.88 djm 250: {
1.107 djm 251: struct sshbuf *buffer = NULL;
252: int r, fd;
1.88 djm 253:
1.107 djm 254: *keyp = NULL;
255: if (commentp != NULL)
256: *commentp = NULL;
1.88 djm 257:
1.107 djm 258: if ((fd = open(filename, O_RDONLY)) < 0)
259: return SSH_ERR_SYSTEM_ERROR;
260: if (sshkey_perm_ok(fd, filename) != 0) {
261: r = SSH_ERR_KEY_BAD_PERMISSIONS;
262: goto out;
1.29 markus 263: }
1.86 djm 264:
1.107 djm 265: if ((buffer = sshbuf_new()) == NULL) {
266: r = SSH_ERR_ALLOC_FAIL;
267: goto out;
1.86 djm 268: }
1.109 djm 269: if ((r = sshkey_load_file(fd, buffer)) != 0 ||
1.117 ! tim 270: (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
! 271: commentp)) != 0)
1.107 djm 272: goto out;
273: r = 0;
274: out:
1.86 djm 275: close(fd);
1.107 djm 276: if (buffer != NULL)
277: sshbuf_free(buffer);
278: return r;
1.18 markus 279: }
280:
1.37 itojun 281: static int
1.107 djm 282: sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
1.18 markus 283: {
284: FILE *f;
1.59 dtucker 285: char line[SSH_MAX_PUBKEY_BYTES];
1.18 markus 286: char *cp;
1.60 dtucker 287: u_long linenum = 0;
1.107 djm 288: int r;
1.18 markus 289:
1.107 djm 290: if (commentp != NULL)
291: *commentp = NULL;
292: if ((f = fopen(filename, "r")) == NULL)
293: return SSH_ERR_SYSTEM_ERROR;
294: while (read_keyfile_line(f, filename, line, sizeof(line),
295: &linenum) != -1) {
296: cp = line;
297: switch (*cp) {
298: case '#':
299: case '\n':
300: case '\0':
301: continue;
302: }
303: /* Abort loading if this looks like a private key */
304: if (strncmp(cp, "-----BEGIN", 10) == 0 ||
305: strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
306: break;
307: /* Skip leading whitespace. */
308: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
309: ;
310: if (*cp) {
311: if ((r = sshkey_read(k, &cp)) == 0) {
312: cp[strcspn(cp, "\r\n")] = '\0';
313: if (commentp) {
314: *commentp = strdup(*cp ?
315: cp : filename);
316: if (*commentp == NULL)
317: r = SSH_ERR_ALLOC_FAIL;
1.18 markus 318: }
1.107 djm 319: fclose(f);
320: return r;
1.18 markus 321: }
322: }
323: }
1.107 djm 324: fclose(f);
325: return SSH_ERR_INVALID_FORMAT;
1.18 markus 326: }
327:
1.29 markus 328: /* load public key from ssh v1 private or any pubkey file */
1.107 djm 329: int
330: sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
1.18 markus 331: {
1.107 djm 332: struct sshkey *pub = NULL;
1.110 deraadt 333: char file[PATH_MAX];
1.107 djm 334: int r, fd;
1.18 markus 335:
1.107 djm 336: if (keyp != NULL)
337: *keyp = NULL;
338: if (commentp != NULL)
339: *commentp = NULL;
340:
1.111 djm 341: /* XXX should load file once and attempt to parse each format */
342:
1.107 djm 343: if ((fd = open(filename, O_RDONLY)) < 0)
344: goto skip;
1.106 markus 345: #ifdef WITH_SSH1
1.53 markus 346: /* try rsa1 private key */
1.109 djm 347: r = sshkey_load_public_rsa1(fd, keyp, commentp);
1.107 djm 348: close(fd);
349: switch (r) {
350: case SSH_ERR_INTERNAL_ERROR:
351: case SSH_ERR_ALLOC_FAIL:
352: case SSH_ERR_INVALID_ARGUMENT:
353: case SSH_ERR_SYSTEM_ERROR:
354: case 0:
355: return r;
356: }
1.113 djm 357: #else /* WITH_SSH1 */
358: close(fd);
1.107 djm 359: #endif /* WITH_SSH1 */
360:
361: /* try ssh2 public key */
362: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
363: return SSH_ERR_ALLOC_FAIL;
364: if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
365: if (keyp != NULL)
366: *keyp = pub;
367: return 0;
368: }
369: sshkey_free(pub);
1.53 markus 370:
1.107 djm 371: #ifdef WITH_SSH1
1.53 markus 372: /* try rsa1 public key */
1.107 djm 373: if ((pub = sshkey_new(KEY_RSA1)) == NULL)
374: return SSH_ERR_ALLOC_FAIL;
375: if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
376: if (keyp != NULL)
377: *keyp = pub;
378: return 0;
379: }
380: sshkey_free(pub);
381: #endif /* WITH_SSH1 */
1.53 markus 382:
1.107 djm 383: skip:
384: /* try .pub suffix */
385: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
386: return SSH_ERR_ALLOC_FAIL;
387: r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */
1.29 markus 388: if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
389: (strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
1.107 djm 390: (r = sshkey_try_load_public(pub, file, commentp)) == 0) {
391: if (keyp != NULL)
392: *keyp = pub;
393: return 0;
394: }
395: sshkey_free(pub);
1.111 djm 396:
1.107 djm 397: return r;
1.81 djm 398: }
399:
400: /* Load the certificate associated with the named private key */
1.107 djm 401: int
402: sshkey_load_cert(const char *filename, struct sshkey **keyp)
1.81 djm 403: {
1.107 djm 404: struct sshkey *pub = NULL;
405: char *file = NULL;
406: int r = SSH_ERR_INTERNAL_ERROR;
407:
408: *keyp = NULL;
1.81 djm 409:
1.107 djm 410: if (asprintf(&file, "%s-cert.pub", filename) == -1)
411: return SSH_ERR_ALLOC_FAIL;
412:
413: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
414: goto out;
415: }
416: if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
417: goto out;
418:
419: *keyp = pub;
420: pub = NULL;
421: r = 0;
422:
423: out:
424: if (file != NULL)
1.97 djm 425: free(file);
1.107 djm 426: if (pub != NULL)
427: sshkey_free(pub);
428: return r;
1.81 djm 429: }
430:
431: /* Load private key and certificate */
1.107 djm 432: int
433: sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
434: struct sshkey **keyp, int *perm_ok)
1.81 djm 435: {
1.107 djm 436: struct sshkey *key = NULL, *cert = NULL;
437: int r;
438:
439: *keyp = NULL;
1.81 djm 440:
441: switch (type) {
1.106 markus 442: #ifdef WITH_OPENSSL
1.81 djm 443: case KEY_RSA:
444: case KEY_DSA:
1.83 djm 445: case KEY_ECDSA:
1.116 markus 446: #endif /* WITH_OPENSSL */
1.101 djm 447: case KEY_ED25519:
1.107 djm 448: case KEY_UNSPEC:
1.81 djm 449: break;
450: default:
1.107 djm 451: return SSH_ERR_KEY_TYPE_UNKNOWN;
1.81 djm 452: }
453:
1.107 djm 454: if ((r = sshkey_load_private_type(type, filename,
455: passphrase, &key, NULL, perm_ok)) != 0 ||
456: (r = sshkey_load_cert(filename, &cert)) != 0)
457: goto out;
1.81 djm 458:
459: /* Make sure the private key matches the certificate */
1.107 djm 460: if (sshkey_equal_public(key, cert) == 0) {
461: r = SSH_ERR_KEY_CERT_MISMATCH;
462: goto out;
1.81 djm 463: }
464:
1.115 djm 465: if ((r = sshkey_to_certified(key)) != 0 ||
1.107 djm 466: (r = sshkey_cert_copy(cert, key)) != 0)
467: goto out;
468: r = 0;
469: *keyp = key;
470: key = NULL;
471: out:
472: if (key != NULL)
473: sshkey_free(key);
474: if (cert != NULL)
475: sshkey_free(cert);
476: return r;
1.1 deraadt 477: }
1.80 djm 478:
479: /*
1.107 djm 480: * Returns success if the specified "key" is listed in the file "filename",
481: * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
1.108 djm 482: * If "strict_type" is set then the key type must match exactly,
1.80 djm 483: * otherwise a comparison that ignores certficiate data is performed.
1.108 djm 484: * If "check_ca" is set and "key" is a certificate, then its CA key is
485: * also checked and sshkey_in_file() will return success if either is found.
1.80 djm 486: */
487: int
1.108 djm 488: sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
489: int check_ca)
1.80 djm 490: {
491: FILE *f;
492: char line[SSH_MAX_PUBKEY_BYTES];
493: char *cp;
494: u_long linenum = 0;
1.107 djm 495: int r = 0;
496: struct sshkey *pub = NULL;
497: int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
498: strict_type ? sshkey_equal : sshkey_equal_public;
1.80 djm 499:
1.108 djm 500: if ((f = fopen(filename, "r")) == NULL)
501: return SSH_ERR_SYSTEM_ERROR;
1.80 djm 502:
503: while (read_keyfile_line(f, filename, line, sizeof(line),
1.107 djm 504: &linenum) != -1) {
1.80 djm 505: cp = line;
506:
507: /* Skip leading whitespace. */
508: for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
509: ;
510:
511: /* Skip comments and empty lines */
512: switch (*cp) {
513: case '#':
514: case '\n':
515: case '\0':
516: continue;
517: }
518:
1.107 djm 519: if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
520: r = SSH_ERR_ALLOC_FAIL;
521: goto out;
1.80 djm 522: }
1.107 djm 523: if ((r = sshkey_read(pub, &cp)) != 0)
524: goto out;
1.108 djm 525: if (sshkey_compare(key, pub) ||
526: (check_ca && sshkey_is_cert(key) &&
527: sshkey_compare(key->cert->signature_key, pub))) {
1.107 djm 528: r = 0;
529: goto out;
1.80 djm 530: }
1.107 djm 531: sshkey_free(pub);
532: pub = NULL;
1.80 djm 533: }
1.107 djm 534: r = SSH_ERR_KEY_NOT_FOUND;
535: out:
536: if (pub != NULL)
537: sshkey_free(pub);
1.80 djm 538: fclose(f);
1.107 djm 539: return r;
1.108 djm 540: }
541:
542: /*
543: * Checks whether the specified key is revoked, returning 0 if not,
544: * SSH_ERR_KEY_REVOKED if it is or another error code if something
545: * unexpected happened.
546: * This will check both the key and, if it is a certificate, its CA key too.
547: * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
548: */
549: int
550: sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
551: {
552: int r;
553:
554: r = ssh_krl_file_contains_key(revoked_keys_file, key);
555: /* If this was not a KRL to begin with then continue below */
556: if (r != SSH_ERR_KRL_BAD_MAGIC)
557: return r;
558:
559: /*
560: * If the file is not a KRL or we can't handle KRLs then attempt to
561: * parse the file as a flat list of keys.
562: */
563: switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
564: case 0:
565: /* Key found => revoked */
566: return SSH_ERR_KEY_REVOKED;
567: case SSH_ERR_KEY_NOT_FOUND:
568: /* Key not found => not revoked */
569: return 0;
570: default:
571: /* Some other error occurred */
572: return r;
573: }
1.80 djm 574: }
1.107 djm 575: