Skip to content
Snippets Groups Projects
cmdgen.c 47.6 KiB
Newer Older
          case SSH_KEYTYPE_SSH2:
          case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
          case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
            if (!load_encrypted) {
                sfree(origcomment);
                origcomment = NULL;
                ssh2blob = strbuf_new();
                if (ppk_loadpub_s(infile_bs, &ssh2alg,
                                  BinarySink_UPCAST(ssh2blob),
                                  &origcomment, &error)) {
                    const ssh_keyalg *alg = find_pubkey_alg(ssh2alg);
                    if (alg)
                        bits = ssh_key_public_bits(
                            alg, ptrlen_from_strbuf(ssh2blob));
                } else {
                    strbuf_free(ssh2blob);
                ssh2key = ppk_load_s(infile_bs, old_passphrase, &error);
            BinarySource_REWIND(infile_bs);
            if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
                error = NULL;
            else if (!error) {
                if (ssh2key == SSH2_WRONG_PASSPHRASE)
                    error = "wrong passphrase";
                else
                    error = "unknown error";
            }
            break;

          case SSH_KEYTYPE_OPENSSH_PEM:
          case SSH_KEYTYPE_OPENSSH_NEW:
          case SSH_KEYTYPE_SSHCOM:
            ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error);
            if (ssh2key) {
                if (ssh2key != SSH2_WRONG_PASSPHRASE)
                    error = NULL;
                else
                    error = "wrong passphrase";
            } else if (!error)
                error = "unknown error";
            break;

          default:
            unreachable("bad input key type");
        }

        if (error) {
            fprintf(stderr, "puttygen: error loading `%s': %s\n",
                    infile, error);
            RETURN(1);
    }

    /*
     * Change the comment if asked to.
     */
    if (comment) {
        if (sshver == 1) {
            assert(ssh1key);
            sfree(ssh1key->comment);
            ssh1key->comment = dupstr(comment);
        } else {
            assert(ssh2key);
            sfree(ssh2key->comment);
            ssh2key->comment = dupstr(comment);
        }
    /*
     * Unless we're changing the passphrase, the old one (if any) is a
     * reasonable default.
     */
    if (!change_passphrase && old_passphrase && !new_passphrase)
        new_passphrase = dupstr(old_passphrase);
    /*
     * Prompt for a new passphrase if we have been asked to, or if
     * we have just generated a key.
     *
     * In the latter case, an exception is if we're producing text
     * output, because that output format doesn't support encryption
     * in any case.
    if (!new_passphrase && (change_passphrase ||
                            (keytype != NOKEYGEN && outtype != TEXT))) {
        prompts_t *p = new_prompts();
        int ret;

        p->to_server = false;
        p->from_server = false;
        p->name = dupstr("New SSH key passphrase");
        add_prompt(p, dupstr("Enter passphrase to save key: "), false);
        add_prompt(p, dupstr("Re-enter passphrase to verify: "), false);
        ret = console_get_userpass_input(p);
        assert(ret >= 0);
        if (!ret) {
            free_prompts(p);
            perror("puttygen: unable to read new passphrase");
            RETURN(1);
            if (strcmp(prompt_get_result_ref(p->prompts[0]),
                       prompt_get_result_ref(p->prompts[1]))) {
                free_prompts(p);
                fprintf(stderr, "puttygen: passphrases do not match\n");
                RETURN(1);
            new_passphrase = prompt_get_result(p->prompts[0]);
    if (new_passphrase && !*new_passphrase) {
        sfree(new_passphrase);
        new_passphrase = NULL;
     * (In the case where outfile and outfiletmp are both NULL,
     * there is no semantic reason to initialise outfilename at
     * all; but we have to write _something_ to it or some compiler
     * will probably complain that it might be used uninitialised.)
     */
    if (outfiletmp)
        outfilename = filename_from_str(outfiletmp);
        outfilename = filename_from_str(outfile ? outfile : "");
        bool ret;
        int real_outtype;
        random_ref(); /* we'll need a few random bytes in the save file */
        if (sshver == 1) {
            assert(ssh1key);
            ret = rsa1_save_f(outfilename, ssh1key, new_passphrase);
            if (!ret) {
                fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
                RETURN(1);
            ret = ppk_save_f(outfilename, ssh2key, new_passphrase, &params);
            if (!ret) {
                fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
                RETURN(1);
            }
        }
        if (outfiletmp) {
            if (!move(outfiletmp, outfile))
                RETURN(1);              /* rename failed */
      case PUBLICO: {
        FILE *fp;

        if (outfile) {
          fp = f_open(outfilename, "w", false);
          if (!fp) {
            fprintf(stderr, "unable to open output file\n");
            exit(1);
          }
        } else {
          fp = stdout;
        }
        if (sshver == 1) {
          ssh1_write_pubkey(fp, ssh1key);
        } else {
          if (!ssh2blob) {
            assert(ssh2key);
            ssh2blob = strbuf_new();
            ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob));
          }

          ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
                            ssh2blob->s, ssh2blob->len,
                            (outtype == PUBLIC ?
                             SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
                             SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
        }
      case FP: {
        FILE *fp;
        char *fingerprint;
        if (sshver == 1) {
          assert(ssh1key);
          fingerprint = rsa_ssh1_fingerprint(ssh1key);
        } else {
          if (ssh2key) {
            fingerprint = ssh2_fingerprint(ssh2key->key, fptype);
          } else {
            assert(ssh2blob);
            fingerprint = ssh2_fingerprint_blob(
                ptrlen_from_strbuf(ssh2blob), fptype);
        if (outfile) {
          fp = f_open(outfilename, "w", false);
          if (!fp) {
            fprintf(stderr, "unable to open output file\n");
            exit(1);
          }
        } else {
          fp = stdout;
        fprintf(fp, "%s\n", fingerprint);
        if (outfile)
            fclose(fp);

        sfree(fingerprint);
        assert(sshver == 2);
        assert(ssh2key);
        random_ref(); /* both foreign key types require randomness,
                       * for IV or padding */
        switch (outtype) {
          case OPENSSH_AUTO:
            real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
            break;
          case OPENSSH_NEW:
            real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
            break;
          case SSHCOM:
            real_outtype = SSH_KEYTYPE_SSHCOM;
            break;
            unreachable("control flow goof");
        ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
        if (!ret) {
            fprintf(stderr, "puttygen: unable to export key\n");
            RETURN(1);
        }
        if (outfiletmp) {
            if (!move(outfiletmp, outfile))
                RETURN(1);              /* rename failed */

      case TEXT: {
        key_components *kc;
        if (sshver == 1) {
            assert(ssh1key);
            kc = rsa_components(ssh1key);
        } else {
            if (ssh2key) {
                kc = ssh_key_components(ssh2key->key);
            } else {
                assert(ssh2blob);

                BinarySource src[1];
                BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ssh2blob));
                ptrlen algname = get_string(src);
                const ssh_keyalg *alg = find_pubkey_alg_len(algname);
                if (!alg) {
                    fprintf(stderr, "puttygen: cannot extract key components "
                            "from public key of unknown type '%.*s'\n",
                            PTRLEN_PRINTF(algname));
                    RETURN(1);
                }
                ssh_key *sk = ssh_key_new_pub(
                    alg, ptrlen_from_strbuf(ssh2blob));
                if (!sk) {
                    fprintf(stderr, "puttygen: unable to decode public key\n");
                    RETURN(1);
                }
                kc = ssh_key_components(sk);
                ssh_key_free(sk);
            }
        }

        FILE *fp;
        if (outfile) {
            fp = f_open(outfilename, "w", false);
            if (!fp) {
                fprintf(stderr, "unable to open output file\n");
                exit(1);
            }
        } else {
            fp = stdout;
        }

        for (size_t i = 0; i < kc->ncomponents; i++) {
            if (kc->components[i].is_mp_int) {
                char *hex = mp_get_hex(kc->components[i].mp);
                fprintf(fp, "%s=0x%s\n", kc->components[i].name, hex);
                smemclr(hex, strlen(hex));
                sfree(hex);
            } else {
                fprintf(fp, "%s=\"", kc->components[i].name);
                write_c_string_literal(fp, ptrlen_from_asciz(
                                           kc->components[i].text));
                fputs("\"\n", fp);
            }
        }

        if (outfile)
            fclose(fp);
        key_components_free(kc);
        break;
      }
        smemclr(old_passphrase, strlen(old_passphrase));
        sfree(old_passphrase);
        smemclr(new_passphrase, strlen(new_passphrase));
        sfree(new_passphrase);
    if (ssh1key) {
        freersakey(ssh1key);
        sfree(ssh1key);
    if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) {
        sfree(ssh2key->comment);
        if (ssh2key->key)
            ssh_key_free(ssh2key->key);
    if (ssh2blob)
        strbuf_free(ssh2blob);
Simon Tatham's avatar
Simon Tatham committed
    sfree(origcomment);
    if (infilename)
        filename_free(infilename);
    if (infile_lf)
        lf_free(infile_lf);
    if (outfilename)
        filename_free(outfilename);
    sfree(outfiletmp);
    return exit_status;