Hi,

Awhile ago I mentioned wanting to get proxy support (RFC 3280, yes it's
expired, but in use) into openssl as simply as possible.

I've built the attached module, that does what I wanted. What's the best way
to try and get this integrated into the standard distribution?

Thanks,

--Ivan
/* apps/x509_proxy.c */
/* Copyright (C) 2006 Ivan R. Judson ([EMAIL PROTECTED])
 * All rights reserved.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef OPENSSL_NO_STDIO
#define APPS_WIN16
#endif
#include "apps.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#include <openssl/bn.h>
#include <openssl/lhash.h>
#ifndef OPENSSL_NO_RSA
#include <openssl/rsa.h>
#endif

#undef PROG
#define PROG x509_proxy_main

#undef POSTFIX
#define POSTFIX ".srl"
#define DEF_DAYS        1

static const char *x509_proxy_usage[]={
  "usage: x509_proxy args\n",
  " -inform arg     - input format - default PEM (one of DER, NET or PEM)\n",
  " -keyform arg    - private key format - default PEM\n",
  " -cert arg       - input certificate\n",
  " -key arg        - input private key\n",
  " -proxy arg      - input proxy certificate\n",
  " -out arg        - output file - default stdout\n",
  " -passin arg     - private key password source\n",
  " -days arg       - How long till expiry of a signed certificate - def 30 
days\n",
  " -md2/-md5/-sha1/-mdc2 - digest to use\n",
  " -create         - create a proxy certificate\n",
  " -info           - print the certificate in text form\n",
  NULL
};

#ifndef OPENSSL_NO_RSA
static int MS_CALLBACK genrsa_cb(int p, int n, BN_GENCB *cb);
#endif

int MAIN(int, char **);

int MAIN(int argc, char **argv)
{
  char *proxyfile=NULL, *outfile=NULL, *keyfile=NULL, *certfile=NULL;
  char *alias=NULL, *passin=NULL, *passargin=NULL;
  const char **pp;
  int ret=1, num, badops=0, informat, keyformat, info=0;
  int create=0, days=DEF_DAYS, modulus=512;
  unsigned long nmflag = 0, certflag = 0;
  const EVP_MD *md_alg, *digest=EVP_md5();
  ASN1_INTEGER *sno = NULL;
  BIO *out=NULL, *STDout=NULL;
  ENGINE *e = NULL;
  EVP_PKEY *userkey=NULL;
  STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL;
  X509 *usercert=NULL, *proxy=NULL;
#ifndef OPENSSL_NO_ENGINE
  char *engine=NULL;
#endif

  apps_startup();

  if (bio_err == NULL)
    bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);

  if (!load_config(bio_err, NULL))
    goto end;
  STDout=BIO_new_fp(stdout,BIO_NOCLOSE);
#ifdef OPENSSL_SYS_VMS
  {
    BIO *tmpbio = BIO_new(BIO_f_linebuffer());
    STDout = BIO_push(tmpbio, STDout);
  }
#endif

  informat=FORMAT_PEM;
  keyformat=FORMAT_PEM;

  argc--;
  argv++;
  num=0;

  while (argc >= 1) {
    if  (strcmp(*argv,"-inform") == 0) {
      if (--argc < 1) goto bad;
      informat=str2fmt(*(++argv));
    } else if (strcmp(*argv,"-keyform") == 0) {
      if (--argc < 1) goto bad;
      keyformat=str2fmt(*(++argv));
    } else if (strcmp(*argv,"-days") == 0) {
      if (--argc < 1) goto bad;
      days=atoi(*(++argv));
      if (days == 0) {
        BIO_printf(STDout,"bad number of days\n");
        goto bad;
      }
    } else if (strcmp(*argv,"-passin") == 0) {
      if (--argc < 1) goto bad;
      passargin= *(++argv);
    } else if (strcmp(*argv,"-cert") == 0) {
      if (--argc < 1) goto bad;
      certfile= *(++argv);
    } else if (strcmp(*argv,"-key") == 0) {
      if (--argc < 1) goto bad;
      keyfile= *(++argv);
    } else if (strcmp(*argv,"-proxy") == 0) {
      if (--argc < 1) goto bad;
      proxyfile= *(++argv);
    } else if (strcmp(*argv,"-out") == 0) {
      if (--argc < 1) goto bad;
      outfile= *(++argv);
    } else if (strcmp(*argv,"-create") == 0)
      create = ++num;
    else if (strcmp(*argv,"-info") == 0)
      info = ++num;
    else if ((md_alg=EVP_get_digestbyname(*argv + 1)))
      digest=md_alg;
    else {
      BIO_printf(bio_err,"unknown option %s\n",*argv);
      badops=1;
      break;
    }
                
    argc--;
    argv++;
  }

  if (badops) {
  bad:
    for (pp=x509_proxy_usage; (*pp != NULL); pp++)
      BIO_printf(bio_err,"%s",*pp);
    goto end;
  }

  /* Initialization */
#ifndef OPENSSL_NO_ENGINE
  e = setup_engine(bio_err, engine, 0);
#endif

  if (create)
    app_RAND_load_file(NULL, bio_err, 0);
  
  ERR_load_crypto_strings();
  
  if (!app_passwd(bio_err, passargin, NULL, &passin, NULL)) {
    BIO_printf(bio_err, "Error getting password\n");
    goto end;
  }

  /* Create output file or filehandle */
  OBJ_create("2.99999.3",
             "SET.ex3","SET x509v3 extension 3");
  
  out=BIO_new(BIO_s_file());
  
  if (out == NULL) {
    ERR_print_errors(bio_err);
    goto end;
  }
  
  if (outfile == NULL) {
    BIO_set_fp(out,stdout,BIO_NOCLOSE);
#ifdef OPENSSL_SYS_VMS
    {
      BIO *tmpbio = BIO_new(BIO_f_linebuffer());
      out = BIO_push(tmpbio, out);
    }
#endif
  } else {
    if (BIO_write_filename(out,outfile) <= 0) {
      perror(outfile);
      goto end;
    }
  }

  /* If print the proxy, print it! */
  if (info) {
    proxy = load_cert(bio_err, proxyfile, informat, NULL, e, "Certificate");
    X509_print_ex(out, proxy, nmflag, certflag);
  }

  /* If create a new proxy, do that... */
  if (create) {
    long tout = 60*60*12*days;
    unsigned long f4=RSA_F4;
    EVP_PKEY *proxykey = EVP_PKEY_new(), *signkey = EVP_PKEY_new();
    LHASH *hash = lh_new(NULL, NULL);
    X509V3_CTX ctx;
    X509_EXTENSION *ku_ext, *pci_ext;
    X509_NAME *sname=NULL;
    X509_NAME_ENTRY *cn=NULL;
    RSA *rsa = RSA_new();
    BN_GENCB cb;
    BIGNUM *bn = BN_new();
    
    /* Create a new X509 Certificate to be the proxy */
    proxy=X509_new();

    /* Load the certificate */
    if (certfile) {
      usercert = load_cert(bio_err, certfile, informat, NULL, e,"Certificate");
      if (usercert == NULL) goto end;
      while(X509_get_ext_count(usercert) > 0) 
        X509_delete_ext(usercert, 0);
    }

    /* Load the private key */
    if (keyfile) {
      userkey = load_key(bio_err, keyfile, FORMAT_PEM, 0, 
                       NULL, e, "Private Key");
    }
    
    /* Generate a new RSA private key */
    BN_GENCB_set(&cb, genrsa_cb, bio_err);
    BIO_printf(bio_err,
               "Generating RSA private key, %d bit long modulus\n",
               modulus);
    if (!BN_set_word(bn, f4) || 
        !RSA_generate_key_ex(rsa, modulus, bn, &cb) ||
        !EVP_PKEY_assign_RSA(proxykey, rsa)) {
      BN_free(bn);
      RSA_free(rsa);
      goto end;
    }

    /* Create the fancy proxy extensions */
    X509V3_set_conf_lhash(&ctx, hash);
    ku_ext = X509V3_EXT_conf(hash, &ctx, "keyUsage", 
                  "Digital Signature, Key Encipherment, Data Encipherment");
    X509_EXTENSION_set_critical(ku_ext, 1);

    pci_ext = X509V3_EXT_conf(hash, &ctx, "proxyCertInfo",
                              "critical, language:Inherit all");
    X509_EXTENSION_set_critical(pci_ext, 1);

    /* Get a serial number, creating one if necessary */
    if (sno) {
      if (!X509_set_serialNumber(proxy, sno)) goto end;
    } else {
      if (!rand_serial(NULL, X509_get_serialNumber(proxy))) goto end;
    }

    /* Assign the bits to the proxy */
    if (!X509_set_pubkey(proxy, proxykey)) goto end;
    if (!X509_set_version(proxy, 2)) goto end;
    if (X509_gmtime_adj(X509_get_notBefore(proxy), (long)0) == NULL) goto end;
    if (X509_gmtime_adj(X509_get_notAfter(proxy),(long)3600*24*days) == NULL) 
      goto end;
    sname = X509_NAME_dup(X509_get_subject_name(usercert));
    if (!X509_NAME_add_entry_by_txt(sname, "commonName", MBSTRING_ASC, 
                                    i2s_ASN1_INTEGER(NULL, 
                                           X509_get_serialNumber(proxy)),
                                    -1, -1, 0))
      goto end;
    if (!X509_set_issuer_name(proxy, X509_get_subject_name(usercert))) 
      goto end;
    if (!X509_set_subject_name(proxy, sname)) 
      goto end;
    if (!X509_add_ext(proxy, ku_ext, 1)) goto end;
    if (!X509_add_ext(proxy, pci_ext, 1)) goto end;

    /* Assign the user supplied private key to the signing key */
    EVP_PKEY_assign_RSA(signkey, EVP_PKEY_get1_RSA(userkey));

    /* sign the proxy */
    if (!X509_sign(proxy, signkey, digest)) {
      ERR_print_errors(bio_err);
      RSA_free(rsa);
      EVP_PKEY_free(proxykey);
      goto end;
    }

    /* Print out the proxy */
    if (!PEM_write_bio_X509(out, proxy)) {
      BIO_printf(bio_err,"Unable to write proxy certificate\n");
      ERR_print_errors(bio_err);
    }
    if (!PEM_write_bio_RSAPrivateKey(out, rsa, NULL, NULL, 0, NULL, NULL)) {
      BIO_printf(bio_err,"Unable to write private key\n");
      ERR_print_errors(bio_err);
    }
    if (!PEM_write_bio_X509(out, usercert)) {
      BIO_printf(bio_err,"Unable to write user certificate\n");
      ERR_print_errors(bio_err);
    }

    RSA_free(rsa);
  }

  ret=0;

  /* Cleanup */
 end:
  if (create)
    app_RAND_write_file(NULL, bio_err);
  OBJ_cleanup();
  BIO_free_all(out);
  BIO_free_all(STDout);
  X509_free(usercert);
  EVP_PKEY_free(userkey);
  ASN1_INTEGER_free(sno);
  if (passin) OPENSSL_free(passin);
  apps_shutdown();
  OPENSSL_EXIT(ret);
}

static ASN1_INTEGER *x509_load_serial(char *CAfile, char *serialfile, int 
create)
{
  char *buf = NULL, *p;
  ASN1_INTEGER *bs = NULL;
  BIGNUM *serial = NULL;
  size_t len;

  len = ((serialfile == NULL)
         ?(strlen(CAfile)+strlen(POSTFIX)+1)
         :(strlen(serialfile)))+1;
  buf=OPENSSL_malloc(len);
  if (buf == NULL) { BIO_printf(bio_err,"out of mem\n"); goto end; }
  if (serialfile == NULL)
    {
      BUF_strlcpy(buf,CAfile,len);
      for (p=buf; *p; p++)
        if (*p == '.')
          {
            *p='\0';
            break;
          }
      BUF_strlcat(buf,POSTFIX,len);
    }
  else
    BUF_strlcpy(buf,serialfile,len);

  serial = load_serial(buf, create, NULL);
  if (serial == NULL) goto end;

  if (!BN_add_word(serial,1))
    { BIO_printf(bio_err,"add_word failure\n"); goto end; }

  if (!save_serial(buf, NULL, serial, &bs)) goto end;

 end:
  if (buf) OPENSSL_free(buf);
  BN_free(serial);
  return bs;
}

#ifndef OPENSSL_NO_RSA
static int MS_CALLBACK genrsa_cb(int p, int n, BN_GENCB *cb)
{
  char c='*';

  if (p == 0) c='.';
  if (p == 1) c='+';
  if (p == 2) c='*';
  if (p == 3) c='\n';
  BIO_write(cb->arg,&c,1);
  (void)BIO_flush(cb->arg);
#ifdef LINT
  p=n;
#endif
  return 1;
}
#endif

Reply via email to