Ruby 1.9.3p327(2012-11-10revision37606)
ext/openssl/ossl_x509name.c
Go to the documentation of this file.
00001 /*
00002  * $Id: ossl_x509name.c 34505 2012-02-09 03:25:07Z nobu $
00003  * 'OpenSSL for Ruby' project
00004  * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz>
00005  * All rights reserved.
00006  */
00007 /*
00008  * This program is licenced under the same licence as Ruby.
00009  * (See the file 'LICENCE'.)
00010  */
00011 #include "ossl.h"
00012 
00013 #define WrapX509Name(klass, obj, name) do { \
00014     if (!(name)) { \
00015         ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \
00016     } \
00017     (obj) = Data_Wrap_Struct((klass), 0, X509_NAME_free, (name)); \
00018 } while (0)
00019 #define GetX509Name(obj, name) do { \
00020     Data_Get_Struct((obj), X509_NAME, (name)); \
00021     if (!(name)) { \
00022         ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \
00023     } \
00024 } while (0)
00025 #define SafeGetX509Name(obj, name) do { \
00026     OSSL_Check_Kind((obj), cX509Name); \
00027     GetX509Name((obj), (name)); \
00028 } while (0)
00029 
00030 #define OBJECT_TYPE_TEMPLATE \
00031   rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE"))
00032 #define DEFAULT_OBJECT_TYPE \
00033   rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE"))
00034 
00035 /*
00036  * Classes
00037  */
00038 VALUE cX509Name;
00039 VALUE eX509NameError;
00040 
00041 /*
00042  * Public
00043  */
00044 VALUE
00045 ossl_x509name_new(X509_NAME *name)
00046 {
00047     X509_NAME *new;
00048     VALUE obj;
00049 
00050     if (!name) {
00051         new = X509_NAME_new();
00052     } else {
00053         new = X509_NAME_dup(name);
00054     }
00055     if (!new) {
00056         ossl_raise(eX509NameError, NULL);
00057     }
00058     WrapX509Name(cX509Name, obj, new);
00059 
00060     return obj;
00061 }
00062 
00063 X509_NAME *
00064 GetX509NamePtr(VALUE obj)
00065 {
00066     X509_NAME *name;
00067 
00068     SafeGetX509Name(obj, name);
00069 
00070     return name;
00071 }
00072 
00073 /*
00074  * Private
00075  */
00076 static VALUE
00077 ossl_x509name_alloc(VALUE klass)
00078 {
00079     X509_NAME *name;
00080     VALUE obj;
00081 
00082     if (!(name = X509_NAME_new())) {
00083         ossl_raise(eX509NameError, NULL);
00084     }
00085     WrapX509Name(klass, obj, name);
00086 
00087     return obj;
00088 }
00089 
00090 static ID id_aref;
00091 static VALUE ossl_x509name_add_entry(int, VALUE*, VALUE);
00092 #define rb_aref(obj, key) rb_funcall((obj), id_aref, 1, (key))
00093 
00094 static VALUE
00095 ossl_x509name_init_i(VALUE i, VALUE args)
00096 {
00097     VALUE self = rb_ary_entry(args, 0);
00098     VALUE template = rb_ary_entry(args, 1);
00099     VALUE entry[3];
00100 
00101     Check_Type(i, T_ARRAY);
00102     entry[0] = rb_ary_entry(i, 0);
00103     entry[1] = rb_ary_entry(i, 1);
00104     entry[2] = rb_ary_entry(i, 2);
00105     if(NIL_P(entry[2])) entry[2] = rb_aref(template, entry[0]);
00106     if(NIL_P(entry[2])) entry[2] = DEFAULT_OBJECT_TYPE;
00107     ossl_x509name_add_entry(3, entry, self);
00108 
00109     return Qnil;
00110 }
00111 
00112 /*
00113  * call-seq:
00114  *    X509::Name.new => name
00115  *    X509::Name.new(string) => name
00116  *    X509::Name.new(dn) => name
00117  *    X509::Name.new(dn, template) => name
00118  */
00119 static VALUE
00120 ossl_x509name_initialize(int argc, VALUE *argv, VALUE self)
00121 {
00122     X509_NAME *name;
00123     VALUE arg, template;
00124 
00125     GetX509Name(self, name);
00126     if (rb_scan_args(argc, argv, "02", &arg, &template) == 0) {
00127         return self;
00128     }
00129     else {
00130         VALUE tmp = rb_check_array_type(arg);
00131         if (!NIL_P(tmp)) {
00132             VALUE args;
00133             if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE;
00134             args = rb_ary_new3(2, self, template);
00135             rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args);
00136         }
00137         else{
00138             const unsigned char *p;
00139             VALUE str = ossl_to_der_if_possible(arg);
00140             X509_NAME *x;
00141             StringValue(str);
00142             p = (unsigned char *)RSTRING_PTR(str);
00143             x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str));
00144             DATA_PTR(self) = name;
00145             if(!x){
00146                 ossl_raise(eX509NameError, NULL);
00147             }
00148         }
00149     }
00150 
00151     return self;
00152 }
00153 
00154 /*
00155  * call-seq:
00156  *    name.add_entry(oid, value [, type]) => self
00157  */
00158 static
00159 VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self)
00160 {
00161     X509_NAME *name;
00162     VALUE oid, value, type;
00163 
00164     rb_scan_args(argc, argv, "21", &oid, &value, &type);
00165     StringValue(oid);
00166     StringValue(value);
00167     if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid);
00168     GetX509Name(self, name);
00169     if (!X509_NAME_add_entry_by_txt(name, RSTRING_PTR(oid), NUM2INT(type),
00170                 (const unsigned char *)RSTRING_PTR(value), RSTRING_LENINT(value), -1, 0)) {
00171         ossl_raise(eX509NameError, NULL);
00172     }
00173 
00174     return self;
00175 }
00176 
00177 static VALUE
00178 ossl_x509name_to_s_old(VALUE self)
00179 {
00180     X509_NAME *name;
00181     char *buf;
00182     VALUE str;
00183 
00184     GetX509Name(self, name);
00185     buf = X509_NAME_oneline(name, NULL, 0);
00186     str = rb_str_new2(buf);
00187     OPENSSL_free(buf);
00188 
00189     return str;
00190 }
00191 
00192 /*
00193  * call-seq:
00194  *    name.to_s => string
00195  *    name.to_s(integer) => string
00196  */
00197 static VALUE
00198 ossl_x509name_to_s(int argc, VALUE *argv, VALUE self)
00199 {
00200     X509_NAME *name;
00201     VALUE flag, str;
00202     BIO *out;
00203     unsigned long iflag;
00204 
00205     rb_scan_args(argc, argv, "01", &flag);
00206     if (NIL_P(flag))
00207         return ossl_x509name_to_s_old(self);
00208     else iflag = NUM2ULONG(flag);
00209     if (!(out = BIO_new(BIO_s_mem())))
00210         ossl_raise(eX509NameError, NULL);
00211     GetX509Name(self, name);
00212     if (!X509_NAME_print_ex(out, name, 0, iflag)){
00213         BIO_free(out);
00214         ossl_raise(eX509NameError, NULL);
00215     }
00216     str = ossl_membio2str(out);
00217 
00218     return str;
00219 }
00220 
00221 /*
00222  * call-seq:
00223  *    name.to_a => [[name, data, type], ...]
00224  */
00225 static VALUE
00226 ossl_x509name_to_a(VALUE self)
00227 {
00228     X509_NAME *name;
00229     X509_NAME_ENTRY *entry;
00230     int i,entries,nid;
00231     char long_name[512];
00232     const char *short_name;
00233     VALUE ary, vname, ret;
00234 
00235     GetX509Name(self, name);
00236     entries = X509_NAME_entry_count(name);
00237     if (entries < 0) {
00238         OSSL_Debug("name entries < 0!");
00239         return rb_ary_new();
00240     }
00241     ret = rb_ary_new2(entries);
00242     for (i=0; i<entries; i++) {
00243         if (!(entry = X509_NAME_get_entry(name, i))) {
00244             ossl_raise(eX509NameError, NULL);
00245         }
00246         if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), entry->object)) {
00247             ossl_raise(eX509NameError, NULL);
00248         }
00249         nid = OBJ_ln2nid(long_name);
00250         if (nid == NID_undef) {
00251             vname = rb_str_new2((const char *) &long_name);
00252         } else {
00253             short_name = OBJ_nid2sn(nid);
00254             vname = rb_str_new2(short_name); /*do not free*/
00255         }
00256         ary = rb_ary_new3(3,
00257                           vname,
00258                           rb_str_new((const char *)entry->value->data, entry->value->length),
00259                           INT2FIX(entry->value->type));
00260         rb_ary_push(ret, ary);
00261     }
00262     return ret;
00263 }
00264 
00265 static int
00266 ossl_x509name_cmp0(VALUE self, VALUE other)
00267 {
00268     X509_NAME *name1, *name2;
00269 
00270     GetX509Name(self, name1);
00271     SafeGetX509Name(other, name2);
00272 
00273     return X509_NAME_cmp(name1, name2);
00274 }
00275 
00276 /*
00277  * call-seq:
00278  *    name.cmp other => integer
00279  *    name.<=> other => integer
00280  *
00281  * Compares this Name with +other+ and returns 0 if they are the same and -1 or
00282  * +1 if they are greater or less than each other respectively.
00283  */
00284 static VALUE
00285 ossl_x509name_cmp(VALUE self, VALUE other)
00286 {
00287     int result;
00288 
00289     result = ossl_x509name_cmp0(self, other);
00290     if (result < 0) return INT2FIX(-1);
00291     if (result > 1) return INT2FIX(1);
00292 
00293     return INT2FIX(0);
00294 }
00295 
00296 static VALUE
00297 ossl_x509name_eql(VALUE self, VALUE other)
00298 {
00299     int result;
00300 
00301     if(CLASS_OF(other) != cX509Name) return Qfalse;
00302     result = ossl_x509name_cmp0(self, other);
00303 
00304     return (result == 0) ? Qtrue : Qfalse;
00305 }
00306 
00307 /*
00308  * call-seq:
00309  *    name.hash => integer
00310  *
00311  * The hash value returned is suitable for use as a certificate's filename in
00312  * a CA path.
00313  */
00314 static VALUE
00315 ossl_x509name_hash(VALUE self)
00316 {
00317     X509_NAME *name;
00318     unsigned long hash;
00319 
00320     GetX509Name(self, name);
00321 
00322     hash = X509_NAME_hash(name);
00323 
00324     return ULONG2NUM(hash);
00325 }
00326 
00327 #ifdef HAVE_X509_NAME_HASH_OLD
00328 /*
00329  * call-seq:
00330  *    name.hash_old => integer
00331  *
00332  * hash_old returns MD5 based hash used in OpenSSL 0.9.X.
00333  */
00334 static VALUE
00335 ossl_x509name_hash_old(VALUE self)
00336 {
00337     X509_NAME *name;
00338     unsigned long hash;
00339 
00340     GetX509Name(self, name);
00341 
00342     hash = X509_NAME_hash_old(name);
00343 
00344     return ULONG2NUM(hash);
00345 }
00346 #endif
00347 
00348 /*
00349  * call-seq:
00350  *    name.to_der => string
00351  */
00352 static VALUE
00353 ossl_x509name_to_der(VALUE self)
00354 {
00355     X509_NAME *name;
00356     VALUE str;
00357     long len;
00358     unsigned char *p;
00359 
00360     GetX509Name(self, name);
00361     if((len = i2d_X509_NAME(name, NULL)) <= 0)
00362         ossl_raise(eX509NameError, NULL);
00363     str = rb_str_new(0, len);
00364     p = (unsigned char *)RSTRING_PTR(str);
00365     if(i2d_X509_NAME(name, &p) <= 0)
00366         ossl_raise(eX509NameError, NULL);
00367     ossl_str_adjust(str, p);
00368 
00369     return str;
00370 }
00371 
00372 /*
00373  * INIT
00374  */
00375 void
00376 Init_ossl_x509name()
00377 {
00378     VALUE utf8str, ptrstr, ia5str, hash;
00379 
00380     id_aref = rb_intern("[]");
00381     eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError);
00382     cX509Name = rb_define_class_under(mX509, "Name", rb_cObject);
00383 
00384     rb_include_module(cX509Name, rb_mComparable);
00385 
00386     rb_define_alloc_func(cX509Name, ossl_x509name_alloc);
00387     rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1);
00388     rb_define_method(cX509Name, "add_entry", ossl_x509name_add_entry, -1);
00389     rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, -1);
00390     rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0);
00391     rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1);
00392     rb_define_alias(cX509Name, "<=>", "cmp");
00393     rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1);
00394     rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0);
00395 #ifdef HAVE_X509_NAME_HASH_OLD
00396     rb_define_method(cX509Name, "hash_old", ossl_x509name_hash_old, 0);
00397 #endif
00398     rb_define_method(cX509Name, "to_der", ossl_x509name_to_der, 0);
00399 
00400     utf8str = INT2NUM(V_ASN1_UTF8STRING);
00401     ptrstr = INT2NUM(V_ASN1_PRINTABLESTRING);
00402     ia5str = INT2NUM(V_ASN1_IA5STRING);
00403     rb_define_const(cX509Name, "DEFAULT_OBJECT_TYPE", utf8str);
00404     hash = rb_hash_new();
00405     RHASH(hash)->ifnone = utf8str;
00406     rb_hash_aset(hash, rb_str_new2("C"), ptrstr);
00407     rb_hash_aset(hash, rb_str_new2("countryName"), ptrstr);
00408     rb_hash_aset(hash, rb_str_new2("serialNumber"), ptrstr);
00409     rb_hash_aset(hash, rb_str_new2("dnQualifier"), ptrstr);
00410     rb_hash_aset(hash, rb_str_new2("DC"), ia5str);
00411     rb_hash_aset(hash, rb_str_new2("domainComponent"), ia5str);
00412     rb_hash_aset(hash, rb_str_new2("emailAddress"), ia5str);
00413     rb_define_const(cX509Name, "OBJECT_TYPE_TEMPLATE", hash);
00414 
00415     rb_define_const(cX509Name, "COMPAT", ULONG2NUM(XN_FLAG_COMPAT));
00416     rb_define_const(cX509Name, "RFC2253", ULONG2NUM(XN_FLAG_RFC2253));
00417     rb_define_const(cX509Name, "ONELINE", ULONG2NUM(XN_FLAG_ONELINE));
00418     rb_define_const(cX509Name, "MULTILINE", ULONG2NUM(XN_FLAG_MULTILINE));
00419 }
00420