=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/ssh-keygen.c,v retrieving revision 1.179 retrieving revision 1.188 diff -u -r1.179 -r1.188 --- src/usr.bin/ssh/ssh-keygen.c 2010/02/26 20:29:54 1.179 +++ src/usr.bin/ssh/ssh-keygen.c 2010/04/23 01:47:41 1.188 @@ -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.188 2010/04/23 01:47:41 djm 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; @@ -94,6 +97,9 @@ /* Path to CA key when certifying keys. */ char *ca_key_path = NULL; +/* Certificate serial number */ +long long cert_serial = 0; + /* Key type when certifying */ u_int cert_key_type = SSH2_CERT_TYPE_USER; @@ -107,18 +113,18 @@ u_int64_t cert_valid_from = 0; u_int64_t cert_valid_to = ~0ULL; -/* Certificate constraints */ -#define CONSTRAINT_X_FWD (1) -#define CONSTRAINT_AGENT_FWD (1<<1) -#define CONSTRAINT_PORT_FWD (1<<2) -#define CONSTRAINT_PTY (1<<3) -#define CONSTRAINT_USER_RC (1<<4) -#define CONSTRAINT_DEFAULT (CONSTRAINT_X_FWD|CONSTRAINT_AGENT_FWD| \ - CONSTRAINT_PORT_FWD|CONSTRAINT_PTY| \ - CONSTRAINT_USER_RC) -u_int32_t constraint_flags = CONSTRAINT_DEFAULT; -char *constraint_command = NULL; -char *constraint_src_addr = NULL; +/* Certificate options */ +#define CRITOPT_X_FWD (1) +#define CRITOPT_AGENT_FWD (1<<1) +#define CRITOPT_PORT_FWD (1<<2) +#define CRITOPT_PTY (1<<3) +#define CRITOPT_USER_RC (1<<4) +#define CRITOPT_DEFAULT (CRITOPT_X_FWD|CRITOPT_AGENT_FWD| \ + CRITOPT_PORT_FWD|CRITOPT_PTY| \ + CRITOPT_USER_RC) +u_int32_t critical_flags = CRITOPT_DEFAULT; +char *critical_command = NULL; +char *critical_src_addr = NULL; /* Dump public key file in format used by real and the original SSH 2 */ int convert_to_ssh2 = 0; @@ -150,9 +156,13 @@ case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; + case KEY_DSA_CERT: + case KEY_DSA_CERT_V00: case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; + case KEY_RSA_CERT: + case KEY_RSA_CERT_V00: case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; @@ -1055,7 +1065,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 +1073,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; } @@ -1094,7 +1103,7 @@ } static void -add_flag_constraint(Buffer *c, const char *name) +add_flag_option(Buffer *c, const char *name) { debug3("%s: %s", __func__, name); buffer_put_cstring(c, name); @@ -1102,7 +1111,7 @@ } static void -add_string_constraint(Buffer *c, const char *name, const char *value) +add_string_option(Buffer *c, const char *name, const char *value) { Buffer b; @@ -1117,24 +1126,23 @@ } static void -prepare_constraint_buf(Buffer *c) +prepare_options_buf(Buffer *c) { - buffer_clear(c); - if ((constraint_flags & CONSTRAINT_X_FWD) != 0) - add_flag_constraint(c, "permit-X11-forwarding"); - if ((constraint_flags & CONSTRAINT_AGENT_FWD) != 0) - add_flag_constraint(c, "permit-agent-forwarding"); - if ((constraint_flags & CONSTRAINT_PORT_FWD) != 0) - add_flag_constraint(c, "permit-port-forwarding"); - if ((constraint_flags & CONSTRAINT_PTY) != 0) - add_flag_constraint(c, "permit-pty"); - 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); - if (constraint_src_addr != NULL) - add_string_constraint(c, "source-address", constraint_src_addr); + if ((critical_flags & CRITOPT_X_FWD) != 0) + add_flag_option(c, "permit-X11-forwarding"); + if ((critical_flags & CRITOPT_AGENT_FWD) != 0) + add_flag_option(c, "permit-agent-forwarding"); + if ((critical_flags & CRITOPT_PORT_FWD) != 0) + add_flag_option(c, "permit-port-forwarding"); + if ((critical_flags & CRITOPT_PTY) != 0) + add_flag_option(c, "permit-pty"); + if ((critical_flags & CRITOPT_USER_RC) != 0) + add_flag_option(c, "permit-user-rc"); + if (critical_command != NULL) + add_string_option(c, "force-command", critical_command); + if (critical_src_addr != NULL) + add_string_option(c, "source-address", critical_src_addr); } static void @@ -1145,12 +1153,32 @@ Key *ca, *public; char *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; + int v00 = 0; /* legacy keys */ tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if ((ca = load_identity(tmp)) == NULL) fatal("Couldn't load CA key \"%s\"", tmp); xfree(tmp); + if (key_type_name != NULL) { + switch (key_type_from_name(key_type_name)) { + case KEY_RSA_CERT_V00: + case KEY_DSA_CERT_V00: + v00 = 1; + break; + case KEY_UNSPEC: + if (strcasecmp(key_type_name, "v00") == 0) { + v00 = 1; + break; + } else if (strcasecmp(key_type_name, "v01") == 0) + break; + /* FALLTHROUGH */ + default: + fprintf(stderr, "unknown key type %s\n", key_type_name); + exit(1); + } + } + for (i = 0; i < argc; i++) { /* Split list of principals */ n = 0; @@ -1173,15 +1201,16 @@ __func__, tmp, key_type(public)); /* Prepare certificate to sign */ - if (key_to_certified(public) != 0) + if (key_to_certified(public, v00) != 0) fatal("Could not upgrade key %s to certificate", tmp); public->cert->type = cert_key_type; + public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); public->cert->nprincipals = n; public->cert->principals = plist; public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; - prepare_constraint_buf(&public->cert->constraints); + prepare_options_buf(&public->cert->critical); public->cert->signature_key = key_from_private(ca); if (key_certify(public, ca) != 0) @@ -1202,13 +1231,14 @@ fprintf(f, " %s\n", comment); fclose(f); - if (!quiet) - logit("Signed %s key %s: id \"%s\"%s%s valid %s", - cert_key_type == SSH2_CERT_TYPE_USER?"user":"host", - out, cert_key_id, + if (!quiet) { + logit("Signed %s key %s: id \"%s\" serial %llu%s%s " + "valid %s", key_cert_type(public), + out, public->cert->key_id, public->cert->serial, 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 +1265,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 +1322,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 == '+') @@ -1295,53 +1341,162 @@ } static void -add_cert_constraint(char *opt) +add_cert_option(char *opt) { char *val; if (strcmp(opt, "clear") == 0) - constraint_flags = 0; + critical_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_X_FWD; + critical_flags &= ~CRITOPT_X_FWD; else if (strcasecmp(opt, "permit-x11-forwarding") == 0) - constraint_flags |= CONSTRAINT_X_FWD; + critical_flags |= CRITOPT_X_FWD; else if (strcasecmp(opt, "no-agent-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_AGENT_FWD; + critical_flags &= ~CRITOPT_AGENT_FWD; else if (strcasecmp(opt, "permit-agent-forwarding") == 0) - constraint_flags |= CONSTRAINT_AGENT_FWD; + critical_flags |= CRITOPT_AGENT_FWD; else if (strcasecmp(opt, "no-port-forwarding") == 0) - constraint_flags &= ~CONSTRAINT_PORT_FWD; + critical_flags &= ~CRITOPT_PORT_FWD; else if (strcasecmp(opt, "permit-port-forwarding") == 0) - constraint_flags |= CONSTRAINT_PORT_FWD; + critical_flags |= CRITOPT_PORT_FWD; else if (strcasecmp(opt, "no-pty") == 0) - constraint_flags &= ~CONSTRAINT_PTY; + critical_flags &= ~CRITOPT_PTY; else if (strcasecmp(opt, "permit-pty") == 0) - constraint_flags |= CONSTRAINT_PTY; + critical_flags |= CRITOPT_PTY; else if (strcasecmp(opt, "no-user-rc") == 0) - constraint_flags &= ~CONSTRAINT_USER_RC; + critical_flags &= ~CRITOPT_USER_RC; else if (strcasecmp(opt, "permit-user-rc") == 0) - constraint_flags |= CONSTRAINT_USER_RC; + critical_flags |= CRITOPT_USER_RC; else if (strncasecmp(opt, "force-command=", 14) == 0) { val = opt + 14; if (*val == '\0') - fatal("Empty force-command constraint"); - if (constraint_command != NULL) + fatal("Empty force-command option"); + if (critical_command != NULL) fatal("force-command already specified"); - constraint_command = xstrdup(val); + critical_command = xstrdup(val); } else if (strncasecmp(opt, "source-address=", 15) == 0) { val = opt + 15; if (*val == '\0') - fatal("Empty source-address constraint"); - if (constraint_src_addr != NULL) + fatal("Empty source-address option"); + if (critical_src_addr != NULL) fatal("source-address already specified"); if (addr_match_cidr_list(NULL, val) != 0) fatal("Invalid source-address list"); - constraint_src_addr = xstrdup(val); + critical_src_addr = xstrdup(val); } else - fatal("Unsupported certificate constraint \"%s\"", opt); + fatal("Unsupported certificate option \"%s\"", opt); } static void +do_show_cert(struct passwd *pw) +{ + Key *key; + struct stat st; + char *key_fp, *ca_fp; + Buffer options, option; + u_char *name, *data; + u_int i, dlen, v00; + + 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); + v00 = key->type == KEY_RSA_CERT_V00 || key->type == KEY_DSA_CERT_V00; + + 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(" Type: %s %s certificate\n", key_ssh_name(key), + key_cert_type(key)); + printf(" Public key: %s %s\n", key_type(key), key_fp); + printf(" Signing CA: %s %s\n", + key_type(key->cert->signature_key), ca_fp); + printf(" Key ID: \"%s\"\n", key->cert->key_id); + if (!v00) + printf(" Serial: %llu\n", key->cert->serial); + 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(" Critical Options: "); + if (buffer_len(&key->cert->critical) == 0) + printf("(none)\n"); + else { + printf("\n"); + buffer_init(&options); + buffer_append(&options, + buffer_ptr(&key->cert->critical), + buffer_len(&key->cert->critical)); + buffer_init(&option); + while (buffer_len(&options) != 0) { + name = buffer_get_string(&options, NULL); + data = buffer_get_string_ptr(&options, &dlen); + buffer_append(&option, 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(&option, NULL); + printf(" %s\n", data); + xfree(data); + } else { + printf(" UNKNOWN OPTION (len %u)\n", + buffer_len(&option)); + buffer_clear(&option); + } + xfree(name); + if (buffer_len(&option) != 0) + fatal("Option corrupt: extra data at end"); + } + buffer_free(&option); + buffer_free(&options); + } + if (!v00) { + printf(" Extensions: "); + if (buffer_len(&key->cert->extensions) == 0) + printf("(none)\n"); + else { + printf("\n"); + buffer_init(&options); + buffer_append(&options, + buffer_ptr(&key->cert->extensions), + buffer_len(&key->cert->extensions)); + buffer_init(&option); + while (buffer_len(&options) != 0) { + name = buffer_get_string(&options, NULL); + (void)buffer_get_string_ptr(&options, &dlen); + printf(" %s UNKNOWN OPTION " + "(len %u)\n", name, dlen); + xfree(name); + } + buffer_free(&option); + buffer_free(&options); + } + } + exit(0); +} + +static void usage(void) { fprintf(stderr, "usage: %s [options]\n", __progname); @@ -1363,11 +1518,12 @@ 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"); fprintf(stderr, " -N phrase Provide new passphrase.\n"); - fprintf(stderr, " -O cnstr Specify a certificate constraint.\n"); + fprintf(stderr, " -O option Specify a certificate option.\n"); fprintf(stderr, " -P phrase Provide old passphrase.\n"); fprintf(stderr, " -p Change passphrase of private key file.\n"); fprintf(stderr, " -q Quiet.\n"); @@ -1381,6 +1537,7 @@ fprintf(stderr, " -v Verbose.\n"); fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n"); fprintf(stderr, " -y Read private key file and print public key.\n"); + fprintf(stderr, " -z serial Specify a serial number.\n"); exit(1); } @@ -1424,8 +1581,8 @@ exit(1); } - while ((opt = getopt(argc, argv, "degiqpclBHhvxXyF:b:f:t:D:I:P:N:n:" - "O:C:r:g:R:T:G:M:S:s:a:V:W:")) != -1) { + 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:z:")) != -1) { switch (opt) { case 'b': bits = (u_int32_t)strtonum(optarg, 768, 32768, &errstr); @@ -1447,6 +1604,9 @@ delete_host = 1; rr_hostname = optarg; break; + case 'L': + show_cert = 1; + break; case 'l': print_fingerprint = 1; break; @@ -1478,7 +1638,7 @@ identity_new_passphrase = optarg; break; case 'O': - add_cert_constraint(optarg); + add_cert_option(optarg); break; case 'C': identity_comment = optarg; @@ -1493,7 +1653,7 @@ break; case 'h': cert_key_type = SSH2_CERT_TYPE_HOST; - constraint_flags = 0; + critical_flags = 0; break; case 'i': case 'X': @@ -1542,9 +1702,8 @@ break; case 'M': memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); - if (errstr) { + if (errstr) fatal("Memory limit is %s: %s", errstr, optarg); - } break; case 'G': do_gen_candidates = 1; @@ -1566,6 +1725,11 @@ case 'V': parse_cert_times(optarg); break; + case 'z': + cert_serial = strtonum(optarg, 0, LLONG_MAX, &errstr); + if (errstr) + fatal("Invalid serial number: %s", errstr); + break; case '?': default: usage(); @@ -1600,6 +1764,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) @@ -1705,13 +1871,19 @@ ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn't already exist. */ - snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); - if (strstr(identity_file, dotsshdir) != NULL && - stat(dotsshdir, &st) < 0) { - if (mkdir(dotsshdir, 0700) < 0) - error("Could not create directory '%s'.", dotsshdir); - else if (!quiet) - printf("Created directory '%s'.\n", dotsshdir); + snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", + pw->pw_dir, _PATH_SSH_USER_DIR); + if (strstr(identity_file, dotsshdir) != NULL) { + if (stat(dotsshdir, &st) < 0) { + if (errno != ENOENT) { + error("Could not stat %s: %s", dotsshdir, + strerror(errno)); + } else if (mkdir(dotsshdir, 0700) < 0) { + error("Could not create directory '%s': %s", + dotsshdir, strerror(errno)); + } else if (!quiet) + printf("Created directory '%s'.\n", dotsshdir); + } } /* If the file already exists, ask the user to confirm. */ if (stat(identity_file, &st) >= 0) {