1#+############################################################################## 2# # 3# File: No/Worries/DN.pm # 4# # 5# Description: Distinguished Names handling without worries # 6# # 7#-############################################################################## 8 9# 10# module definition 11# 12 13package No::Worries::DN; 14use strict; 15use warnings; 16our $VERSION = "1.6"; 17our $REVISION = sprintf("%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/); 18 19# 20# used modules 21# 22 23use No::Worries::Die qw(dief); 24use No::Worries::Export qw(export_control); 25use Params::Validate qw(validate_pos :types); 26 27# 28# constants 29# 30 31use constant FORMAT_RFC2253 => "rfc2253"; 32use constant FORMAT_JAVA => "java"; 33use constant FORMAT_OPENSSL => "openssl"; 34 35# 36# global variables 37# 38 39our( 40 %_Map, # map of known attribute types 41 $_TypeRE, # regexp for a valid attribute type 42 $_ValueRE, # regexp for a valid attribute value 43); 44 45foreach my $type (qw(Email emailAddress EMAILADDRESS)) { 46 $_Map{$type} = $type; 47} 48 49foreach my $type (qw(C CN DC L O OU ST)) { 50 $_Map{$type} = $_Map{lc($type)} = $type; 51} 52 53$_TypeRE = join("|", keys(%_Map)); 54 55$_ValueRE = "[" . 56 "0-9a-zA-Z" . # alphanumerical 57 "\\x20" . # space 58 "\\x27" . # quote 59 "\\x28" . # left parenthesis 60 "\\x29" . # right parenthesis 61 "\\x2d" . # dash 62 "\\x2e" . # dot 63 "\\x2f" . # slash 64 "\\x3a" . # colon 65 "\\x40" . # at sign 66 "\\x5f" . # underscore 67 "\\xa0-\\xff" . # some high-bit characters that may come from ISO-8859-1 68"]+"; 69 70# 71# parse a string containing a DN and return an array reference 72# 73 74sub dn_parse ($) { 75 my($string) = @_; 76 my($sep, @list, @dn); 77 78 validate_pos(@_, { type => SCALAR }); 79 if ($string =~ m/^(\/[a-z]+=[^=]*){3,}$/i) { 80 $sep = "/"; 81 } elsif ($string =~ m/^[a-z]+=[^=]*(,[a-z]+=[^=]*){2,}$/i) { 82 $sep = ","; 83 } elsif ($string =~ m/^[a-z]+=[^=]*(, [a-z]+=[^=]*){2,}$/i) { 84 $sep = ", "; 85 } else { 86 dief("unexpected DN: %s", $string); 87 } 88 @list = split(/$sep/, $string); 89 shift(@list) if $sep eq "/"; 90 @dn = (); 91 foreach my $attr (@list) { 92 if ($attr =~ /^($_TypeRE)=($_ValueRE)$/) { 93 # type=value 94 push(@dn, "$_Map{$1}=$2"); 95 } elsif (@dn and $attr =~ /^($_ValueRE)$/) { 96 # value only, assumed to come from previous attribute 97 $dn[-1] .= $sep . $attr; 98 } else { 99 dief("invalid DN: %s", $string); 100 } 101 } 102 @dn = reverse(@dn) if $sep eq "/"; 103 return(\@dn); 104} 105 106# 107# convert the given parsed DN into a string 108# 109 110sub dn_string ($$) { 111 my($dn, $format) = @_; 112 113 validate_pos(@_, { type => ARRAYREF }, { type => SCALAR }); 114 return(join(",", @{ $dn })) if $format eq FORMAT_RFC2253; 115 return(join(", ", @{ $dn })) if $format eq FORMAT_JAVA; 116 return(join("/", "", reverse(@{ $dn }))) if $format eq FORMAT_OPENSSL; 117 dief("unsupported DN format: %s", $format); 118} 119 120# 121# export control 122# 123 124sub import : method { 125 my($pkg, %exported); 126 127 $pkg = shift(@_); 128 grep($exported{$_}++, map("dn_$_", qw(parse string))); 129 export_control(scalar(caller()), $pkg, \%exported, @_); 130} 131 1321; 133 134__DATA__ 135 136=head1 NAME 137 138No::Worries::DN - Distinguished Names handling without worries 139 140=head1 SYNOPSIS 141 142 use No::Worries::DN qw(dn_parse dn_string); 143 144 $dn = dn_parse("/C=US/O=Acme Corporation/CN=John Doe"); 145 $string = dn_string($dn, No::Worries::DN::FORMAT_JAVA); 146 147=head1 DESCRIPTION 148 149This module eases Distinguished Names (DNs) handling by providing 150convenient functions to parse and convert DNs from and to different 151formats. All the functions die() on error. 152 153=head1 FUNCTIONS 154 155This module provides the following functions (none of them being 156exported by default): 157 158=over 159 160=item dn_parse(STRING) 161 162parse a string containing DN information and return an array reference 163 164=item dn_string(DN, FORMAT) 165 166convert the given parsed DN (an array reference) into a string of the 167given format, this is somehow the opposite of dn_parse() 168 169=back 170 171=head1 FORMATS 172 173Here are the supported formats: 174 175=over 176 177=item No::Worries::DN::FORMAT_RFC2253 178 179this is the format defined by RFC 2253, for instance: 180C<CN=John Doe,O=Acme Corporation,C=US> 181 182=item No::Worries::DN::FORMAT_JAVA 183 184this is a variant of RFC 2253, with extra spaces, for instance: 185C<CN=John Doe, O=Acme Corporation, C=US> 186 187=item No::Worries::DN::FORMAT_OPENSSL 188 189this is the default format used by OpenSSL, for instance: 190C</C=US/O=Acme Corporation/CN=John Doe> 191 192=back 193 194=head1 SEE ALSO 195 196L<No::Worries>. 197 198=head1 AUTHOR 199 200Lionel Cons L<http://cern.ch/lionel.cons> 201 202Copyright (C) CERN 2012-2019 203