=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/ssh-keygen.c,v retrieving revision 1.179 retrieving revision 1.185 diff -u -r1.179 -r1.185 --- src/usr.bin/ssh/ssh-keygen.c 2010/02/26 20:29:54 1.179 +++ src/usr.bin/ssh/ssh-keygen.c 2010/03/15 19:40:02 1.185 @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.179 2010/02/26 20:29:54 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.185 2010/03/15 19:40:02 stevesk Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -74,6 +74,9 @@ /* Flag indicating that we want to delete a host from a known_hosts file */ int delete_host = 0; +/* Flag indicating that we want to show the contents of a certificate */ +int show_cert = 0; + /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; int print_bubblebabble = 0; @@ -1055,7 +1058,7 @@ } static const char * -fmt_validity(void) +fmt_validity(u_int64_t valid_from, u_int64_t valid_to) { char from[32], to[32]; static char ret[64]; @@ -1063,28 +1066,27 @@ struct tm *tm; *from = *to = '\0'; - if (cert_valid_from == 0 && - cert_valid_to == 0xffffffffffffffffULL) + if (valid_from == 0 && valid_to == 0xffffffffffffffffULL) return "forever"; - if (cert_valid_from != 0) { + if (valid_from != 0) { /* XXX revisit INT_MAX in 2038 :) */ - tt = cert_valid_from > INT_MAX ? INT_MAX : cert_valid_from; + tt = valid_from > INT_MAX ? INT_MAX : valid_from; tm = localtime(&tt); strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm); } - if (cert_valid_to != 0xffffffffffffffffULL) { + if (valid_to != 0xffffffffffffffffULL) { /* XXX revisit INT_MAX in 2038 :) */ - tt = cert_valid_to > INT_MAX ? INT_MAX : cert_valid_to; + tt = valid_to > INT_MAX ? INT_MAX : valid_to; tm = localtime(&tt); strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm); } - if (cert_valid_from == 0) { + if (valid_from == 0) { snprintf(ret, sizeof(ret), "before %s", to); return ret; } - if (cert_valid_to == 0xffffffffffffffffULL) { + if (valid_to == 0xffffffffffffffffULL) { snprintf(ret, sizeof(ret), "after %s", from); return ret; } @@ -1132,7 +1134,7 @@ if ((constraint_flags & CONSTRAINT_USER_RC) != 0) add_flag_constraint(c, "permit-user-rc"); if (constraint_command != NULL) - add_string_constraint(c, "forced-command", constraint_command); + add_string_constraint(c, "force-command", constraint_command); if (constraint_src_addr != NULL) add_string_constraint(c, "source-address", constraint_src_addr); } @@ -1208,7 +1210,7 @@ out, cert_key_id, cert_principals != NULL ? " for " : "", cert_principals != NULL ? cert_principals : "", - fmt_validity()); + fmt_validity(cert_valid_from, cert_valid_to)); key_free(public); xfree(out); @@ -1235,13 +1237,29 @@ { struct tm tm; time_t tt; + char buf[32], *fmt; - if (strlen(s) != 8 && strlen(s) != 14) + /* + * POSIX strptime says "The application shall ensure that there + * is white-space or other non-alphanumeric characters between + * any two conversion specifications" so arrange things this way. + */ + switch (strlen(s)) { + case 8: + fmt = "%Y-%m-%d"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); + break; + case 14: + fmt = "%Y-%m-%dT%H:%M:%S"; + snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", + s, s + 4, s + 6, s + 8, s + 10, s + 12); + break; + default: fatal("Invalid certificate time format %s", s); + } bzero(&tm, sizeof(tm)); - if (strptime(s, - strlen(s) == 8 ? "%Y%m%d" : "%Y%m%d%H%M%S", &tm) == NULL) + if (strptime(buf, fmt, &tm) == NULL) fatal("Invalid certificate time %s", s); if ((tt = mktime(&tm)) < 0) fatal("Certificate time %s cannot be represented", s); @@ -1276,7 +1294,7 @@ from = xstrdup(timespec); to = strchr(from, ':'); if (to == NULL || from == to || *(to + 1) == '\0') - fatal("Invalid certificate life specification %s", optarg); + fatal("Invalid certificate life specification %s", timespec); *to++ = '\0'; if (*from == '-' || *from == '+') @@ -1342,6 +1360,90 @@ } static void +do_show_cert(struct passwd *pw) +{ + Key *key; + struct stat st; + char *key_fp, *ca_fp; + Buffer constraints, constraint; + u_char *name, *data; + u_int i, dlen; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) { + perror(identity_file); + exit(1); + } + if ((key = key_load_public(identity_file, NULL)) == NULL) + fatal("%s is not a public key", identity_file); + if (!key_is_cert(key)) + fatal("%s is not a certificate", identity_file); + + key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + ca_fp = key_fingerprint(key->cert->signature_key, + SSH_FP_MD5, SSH_FP_HEX); + + printf("%s:\n", identity_file); + printf(" %s %s certificate %s\n", key_type(key), + key_cert_type(key), key_fp); + printf(" Signed by %s CA %s\n", + key_type(key->cert->signature_key), ca_fp); + printf(" Key ID \"%s\"\n", key->cert->key_id); + printf(" Valid: %s\n", + fmt_validity(key->cert->valid_after, key->cert->valid_before)); + printf(" Principals: "); + if (key->cert->nprincipals == 0) + printf("(none)\n"); + else { + for (i = 0; i < key->cert->nprincipals; i++) + printf("\n %s", + key->cert->principals[i]); + printf("\n"); + } + printf(" Constraints: "); + if (buffer_len(&key->cert->constraints) == 0) + printf("(none)\n"); + else { + printf("\n"); + buffer_init(&constraints); + buffer_append(&constraints, + buffer_ptr(&key->cert->constraints), + buffer_len(&key->cert->constraints)); + buffer_init(&constraint); + while (buffer_len(&constraints) != 0) { + name = buffer_get_string(&constraints, NULL); + data = buffer_get_string_ptr(&constraints, &dlen); + buffer_append(&constraint, data, dlen); + printf(" %s", name); + if (strcmp(name, "permit-X11-forwarding") == 0 || + strcmp(name, "permit-agent-forwarding") == 0 || + strcmp(name, "permit-port-forwarding") == 0 || + strcmp(name, "permit-pty") == 0 || + strcmp(name, "permit-user-rc") == 0) + printf("\n"); + else if (strcmp(name, "force-command") == 0 || + strcmp(name, "source-address") == 0) { + data = buffer_get_string(&constraint, NULL); + printf(" %s\n", data); + xfree(data); + } else { + printf(" UNKNOWN CONSTRAINT (len %u)\n", + buffer_len(&constraint)); + buffer_clear(&constraint); + } + xfree(name); + if (buffer_len(&constraint) != 0) + fatal("Constraint corrupt: extra data at end"); + } + buffer_free(&constraint); + buffer_free(&constraints); + } + + exit(0); +} + +static void usage(void) { fprintf(stderr, "usage: %s [options]\n", __progname); @@ -1363,6 +1465,7 @@ fprintf(stderr, " -h Generate host certificate instead of a user certificate.\n"); fprintf(stderr, " -I key_id Key identifier to include in certificate.\n"); fprintf(stderr, " -i Convert RFC 4716 to OpenSSH key file.\n"); + fprintf(stderr, " -L Print the contents of a certificate.\n"); fprintf(stderr, " -l Show fingerprint of key file.\n"); fprintf(stderr, " -M memory Amount of memory (MB) to use for generating DH-GEX moduli.\n"); fprintf(stderr, " -n name,... User/host principal names to include in certificate\n"); @@ -1424,7 +1527,7 @@ exit(1); } - while ((opt = getopt(argc, argv, "degiqpclBHhvxXyF:b:f:t:D:I:P:N:n:" + while ((opt = getopt(argc, argv, "degiqpclBHLhvxXyF:b:f:t:D:I:P:N:n:" "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) { switch (opt) { case 'b': @@ -1447,6 +1550,9 @@ delete_host = 1; rr_hostname = optarg; break; + case 'L': + show_cert = 1; + break; case 'l': print_fingerprint = 1; break; @@ -1600,6 +1706,8 @@ fatal("Must specify key id (-I) when certifying"); do_ca_sign(pw, argc, argv); } + if (show_cert) + do_show_cert(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); if (print_fingerprint || print_bubblebabble)