Ruby  2.0.0p247(2013-06-27revision41674)
ext/openssl/ossl_x509name.c
Go to the documentation of this file.
00001 /*
00002  * $Id: ossl_x509name.c 38268 2012-12-08 00:26:56Z drbrain $
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(der)                          => name
00116  *    X509::Name.new(distinguished_name)           => name
00117  *    X509::Name.new(distinguished_name, template) => name
00118  *
00119  * Creates a new Name.
00120  *
00121  * A name may be created from a DER encoded string +der+, an Array
00122  * representing a +distinguished_name+ or a +distinguished_name+ along with a
00123  * +template+.
00124  *
00125  *   name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']]
00126  *
00127  *   name = OpenSSL::X509::Name.new name.to_der
00128  *
00129  * See add_entry for a description of the +distinguished_name+ Array's
00130  * contents
00131  */
00132 static VALUE
00133 ossl_x509name_initialize(int argc, VALUE *argv, VALUE self)
00134 {
00135     X509_NAME *name;
00136     VALUE arg, template;
00137 
00138     GetX509Name(self, name);
00139     if (rb_scan_args(argc, argv, "02", &arg, &template) == 0) {
00140         return self;
00141     }
00142     else {
00143         VALUE tmp = rb_check_array_type(arg);
00144         if (!NIL_P(tmp)) {
00145             VALUE args;
00146             if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE;
00147             args = rb_ary_new3(2, self, template);
00148             rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args);
00149         }
00150         else{
00151             const unsigned char *p;
00152             VALUE str = ossl_to_der_if_possible(arg);
00153             X509_NAME *x;
00154             StringValue(str);
00155             p = (unsigned char *)RSTRING_PTR(str);
00156             x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str));
00157             DATA_PTR(self) = name;
00158             if(!x){
00159                 ossl_raise(eX509NameError, NULL);
00160             }
00161         }
00162     }
00163 
00164     return self;
00165 }
00166 
00167 /*
00168  * call-seq:
00169  *    name.add_entry(oid, value [, type]) => self
00170  *
00171  * Adds a new entry with the given +oid+ and +value+ to this name.  The +oid+
00172  * is an object identifier defined in ASN.1.  Some common OIDs are:
00173  *
00174  * C::  Country Name
00175  * CN:: Common Name
00176  * DC:: Domain Component
00177  * O::  Organization Name
00178  * OU:: Organizational Unit Name
00179  * ST:: State or Province Name
00180  */
00181 static
00182 VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self)
00183 {
00184     X509_NAME *name;
00185     VALUE oid, value, type;
00186 
00187     rb_scan_args(argc, argv, "21", &oid, &value, &type);
00188     StringValue(oid);
00189     StringValue(value);
00190     if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid);
00191     GetX509Name(self, name);
00192     if (!X509_NAME_add_entry_by_txt(name, RSTRING_PTR(oid), NUM2INT(type),
00193                 (const unsigned char *)RSTRING_PTR(value), RSTRING_LENINT(value), -1, 0)) {
00194         ossl_raise(eX509NameError, NULL);
00195     }
00196 
00197     return self;
00198 }
00199 
00200 static VALUE
00201 ossl_x509name_to_s_old(VALUE self)
00202 {
00203     X509_NAME *name;
00204     char *buf;
00205     VALUE str;
00206 
00207     GetX509Name(self, name);
00208     buf = X509_NAME_oneline(name, NULL, 0);
00209     str = rb_str_new2(buf);
00210     OPENSSL_free(buf);
00211 
00212     return str;
00213 }
00214 
00215 /*
00216  * call-seq:
00217  *    name.to_s => string
00218  *    name.to_s(flags) => string
00219  *
00220  * Returns this name as a Distinguished Name string.  +flags+ may be one of:
00221  *
00222  * * OpenSSL::X509::Name::COMPAT
00223  * * OpenSSL::X509::Name::RFC2253
00224  * * OpenSSL::X509::Name::ONELINE
00225  * * OpenSSL::X509::Name::MULTILINE
00226  */
00227 static VALUE
00228 ossl_x509name_to_s(int argc, VALUE *argv, VALUE self)
00229 {
00230     X509_NAME *name;
00231     VALUE flag, str;
00232     BIO *out;
00233     unsigned long iflag;
00234 
00235     rb_scan_args(argc, argv, "01", &flag);
00236     if (NIL_P(flag))
00237         return ossl_x509name_to_s_old(self);
00238     else iflag = NUM2ULONG(flag);
00239     if (!(out = BIO_new(BIO_s_mem())))
00240         ossl_raise(eX509NameError, NULL);
00241     GetX509Name(self, name);
00242     if (!X509_NAME_print_ex(out, name, 0, iflag)){
00243         BIO_free(out);
00244         ossl_raise(eX509NameError, NULL);
00245     }
00246     str = ossl_membio2str(out);
00247 
00248     return str;
00249 }
00250 
00251 /*
00252  * call-seq:
00253  *    name.to_a => [[name, data, type], ...]
00254  *
00255  * Returns an Array representation of the distinguished name suitable for
00256  * passing to ::new
00257  */
00258 static VALUE
00259 ossl_x509name_to_a(VALUE self)
00260 {
00261     X509_NAME *name;
00262     X509_NAME_ENTRY *entry;
00263     int i,entries,nid;
00264     char long_name[512];
00265     const char *short_name;
00266     VALUE ary, vname, ret;
00267 
00268     GetX509Name(self, name);
00269     entries = X509_NAME_entry_count(name);
00270     if (entries < 0) {
00271         OSSL_Debug("name entries < 0!");
00272         return rb_ary_new();
00273     }
00274     ret = rb_ary_new2(entries);
00275     for (i=0; i<entries; i++) {
00276         if (!(entry = X509_NAME_get_entry(name, i))) {
00277             ossl_raise(eX509NameError, NULL);
00278         }
00279         if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), entry->object)) {
00280             ossl_raise(eX509NameError, NULL);
00281         }
00282         nid = OBJ_ln2nid(long_name);
00283         if (nid == NID_undef) {
00284             vname = rb_str_new2((const char *) &long_name);
00285         } else {
00286             short_name = OBJ_nid2sn(nid);
00287             vname = rb_str_new2(short_name); /*do not free*/
00288         }
00289         ary = rb_ary_new3(3,
00290                           vname,
00291                           rb_str_new((const char *)entry->value->data, entry->value->length),
00292                           INT2FIX(entry->value->type));
00293         rb_ary_push(ret, ary);
00294     }
00295     return ret;
00296 }
00297 
00298 static int
00299 ossl_x509name_cmp0(VALUE self, VALUE other)
00300 {
00301     X509_NAME *name1, *name2;
00302 
00303     GetX509Name(self, name1);
00304     SafeGetX509Name(other, name2);
00305 
00306     return X509_NAME_cmp(name1, name2);
00307 }
00308 
00309 /*
00310  * call-seq:
00311  *    name.cmp other => integer
00312  *    name.<=> other => integer
00313  *
00314  * Compares this Name with +other+ and returns 0 if they are the same and -1 or
00315  * +1 if they are greater or less than each other respectively.
00316  */
00317 static VALUE
00318 ossl_x509name_cmp(VALUE self, VALUE other)
00319 {
00320     int result;
00321 
00322     result = ossl_x509name_cmp0(self, other);
00323     if (result < 0) return INT2FIX(-1);
00324     if (result > 1) return INT2FIX(1);
00325 
00326     return INT2FIX(0);
00327 }
00328 
00329 /*
00330  * call-seq:
00331  *   name.eql? other => boolean
00332  *
00333  * Returns true if +name+ and +other+ refer to the same hash key.
00334  */
00335 static VALUE
00336 ossl_x509name_eql(VALUE self, VALUE other)
00337 {
00338     int result;
00339 
00340     if(CLASS_OF(other) != cX509Name) return Qfalse;
00341     result = ossl_x509name_cmp0(self, other);
00342 
00343     return (result == 0) ? Qtrue : Qfalse;
00344 }
00345 
00346 /*
00347  * call-seq:
00348  *    name.hash => integer
00349  *
00350  * The hash value returned is suitable for use as a certificate's filename in
00351  * a CA path.
00352  */
00353 static VALUE
00354 ossl_x509name_hash(VALUE self)
00355 {
00356     X509_NAME *name;
00357     unsigned long hash;
00358 
00359     GetX509Name(self, name);
00360 
00361     hash = X509_NAME_hash(name);
00362 
00363     return ULONG2NUM(hash);
00364 }
00365 
00366 #ifdef HAVE_X509_NAME_HASH_OLD
00367 /*
00368  * call-seq:
00369  *    name.hash_old => integer
00370  *
00371  * Returns an MD5 based hash used in OpenSSL 0.9.X.
00372  */
00373 static VALUE
00374 ossl_x509name_hash_old(VALUE self)
00375 {
00376     X509_NAME *name;
00377     unsigned long hash;
00378 
00379     GetX509Name(self, name);
00380 
00381     hash = X509_NAME_hash_old(name);
00382 
00383     return ULONG2NUM(hash);
00384 }
00385 #endif
00386 
00387 /*
00388  * call-seq:
00389  *    name.to_der => string
00390  *
00391  * Converts the name to DER encoding
00392  */
00393 static VALUE
00394 ossl_x509name_to_der(VALUE self)
00395 {
00396     X509_NAME *name;
00397     VALUE str;
00398     long len;
00399     unsigned char *p;
00400 
00401     GetX509Name(self, name);
00402     if((len = i2d_X509_NAME(name, NULL)) <= 0)
00403         ossl_raise(eX509NameError, NULL);
00404     str = rb_str_new(0, len);
00405     p = (unsigned char *)RSTRING_PTR(str);
00406     if(i2d_X509_NAME(name, &p) <= 0)
00407         ossl_raise(eX509NameError, NULL);
00408     ossl_str_adjust(str, p);
00409 
00410     return str;
00411 }
00412 
00413 /*
00414  * Document-class: OpenSSL::X509::Name
00415  *
00416  * An X.509 name represents a hostname, email address or other entity
00417  * associated with a public key.
00418  *
00419  * You can create a Name by parsing a distinguished name String or by
00420  * supplying the distinguished name as an Array.
00421  *
00422  *   name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
00423  *
00424  *   name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']]
00425  */
00426 
00427 void
00428 Init_ossl_x509name()
00429 {
00430     VALUE utf8str, ptrstr, ia5str, hash;
00431 
00432     id_aref = rb_intern("[]");
00433     eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError);
00434     cX509Name = rb_define_class_under(mX509, "Name", rb_cObject);
00435 
00436     rb_include_module(cX509Name, rb_mComparable);
00437 
00438     rb_define_alloc_func(cX509Name, ossl_x509name_alloc);
00439     rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1);
00440     rb_define_method(cX509Name, "add_entry", ossl_x509name_add_entry, -1);
00441     rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, -1);
00442     rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0);
00443     rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1);
00444     rb_define_alias(cX509Name, "<=>", "cmp");
00445     rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1);
00446     rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0);
00447 #ifdef HAVE_X509_NAME_HASH_OLD
00448     rb_define_method(cX509Name, "hash_old", ossl_x509name_hash_old, 0);
00449 #endif
00450     rb_define_method(cX509Name, "to_der", ossl_x509name_to_der, 0);
00451 
00452     utf8str = INT2NUM(V_ASN1_UTF8STRING);
00453     ptrstr = INT2NUM(V_ASN1_PRINTABLESTRING);
00454     ia5str = INT2NUM(V_ASN1_IA5STRING);
00455 
00456     /* Document-const: DEFAULT_OBJECT_TYPE
00457      *
00458      * The default object type for name entries.
00459      */
00460     rb_define_const(cX509Name, "DEFAULT_OBJECT_TYPE", utf8str);
00461     hash = rb_hash_new();
00462     RHASH(hash)->ifnone = utf8str;
00463     rb_hash_aset(hash, rb_str_new2("C"), ptrstr);
00464     rb_hash_aset(hash, rb_str_new2("countryName"), ptrstr);
00465     rb_hash_aset(hash, rb_str_new2("serialNumber"), ptrstr);
00466     rb_hash_aset(hash, rb_str_new2("dnQualifier"), ptrstr);
00467     rb_hash_aset(hash, rb_str_new2("DC"), ia5str);
00468     rb_hash_aset(hash, rb_str_new2("domainComponent"), ia5str);
00469     rb_hash_aset(hash, rb_str_new2("emailAddress"), ia5str);
00470 
00471     /* Document-const: OBJECT_TYPE_TEMPLATE
00472      *
00473      * The default object type template for name entries.
00474      */
00475     rb_define_const(cX509Name, "OBJECT_TYPE_TEMPLATE", hash);
00476 
00477     /* Document-const: COMPAT
00478      *
00479      * A flag for #to_s.
00480      *
00481      * Breaks the name returned into multiple lines if longer than 80
00482      * characters.
00483      */
00484     rb_define_const(cX509Name, "COMPAT", ULONG2NUM(XN_FLAG_COMPAT));
00485 
00486     /* Document-const: RFC2253
00487      *
00488      * A flag for #to_s.
00489      *
00490      * Returns an RFC2253 format name.
00491      */
00492     rb_define_const(cX509Name, "RFC2253", ULONG2NUM(XN_FLAG_RFC2253));
00493 
00494     /* Document-const: ONELINE
00495      *
00496      * A flag for #to_s.
00497      *
00498      * Returns a more readable format than RFC2253.
00499      */
00500     rb_define_const(cX509Name, "ONELINE", ULONG2NUM(XN_FLAG_ONELINE));
00501 
00502     /* Document-const: MULTILINE
00503      *
00504      * A flag for #to_s.
00505      *
00506      * Returns a multiline format.
00507      */
00508     rb_define_const(cX509Name, "MULTILINE", ULONG2NUM(XN_FLAG_MULTILINE));
00509 }
00510