4 use base qw( FS::svc_Common );
6 #use FS::Record qw( qsearch qsearchs );
11 FS::svc_cert - Object methods for svc_cert records
17 $record = new FS::svc_cert \%hash;
18 $record = new FS::svc_cert { 'column' => 'value' };
20 $error = $record->insert;
22 $error = $new_record->replace($old_record);
24 $error = $record->delete;
26 $error = $record->check;
30 An FS::svc_cert object represents a certificate. FS::svc_cert inherits from
31 FS::Record. The following fields are currently supported:
67 =item organization_unit
96 Creates a new certificate. To add the certificate to the database, see L<"insert">.
98 Note that this stores the hash reference, not a distinct copy of the hash it
99 points to. You can ask the object for a copy with the I<hash> method.
103 # the new method can be inherited from FS::Record, if a table method is defined
105 sub table { 'svc_cert'; }
108 my %dis = ( disable_default=>1, disable_fixed=>1, disable_inventory=>1, disable_select=>1 );
110 'name' => 'Certificate',
111 'name_plural' => 'Certificates',
112 'longname_plural' => 'Example services', #optional
113 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
114 'display_weight' => 25,
115 'cancel_weight' => 65,
118 'privatekey' => { label=>'Private key', %dis, },
119 'csr' => { label=>'Certificate signing request', %dis, },
120 'certificate' => { label=>'Certificate', %dis, },
121 'cacert' => { label=>'Certificate authority chain', %dis, },
122 'common_name' => { label=>'Common name', %dis, },
123 'organization' => { label=>'Organization', %dis, },
124 'organization_unit' => { label=>'Organization Unit', %dis, },
125 'city' => { label=>'City', %dis, },
126 'state' => { label=>'State', %dis, },
127 'country' => { label=>'Country', %dis, },
128 'cert_contact' => { label=>'Contact email', %dis, },
130 #'another_field' => {
131 # 'label' => 'Description',
132 # 'def_label' => 'Description for service definitions',
134 # 'disable_default' => 1, #disable switches
135 # 'disable_fixed' => 1, #
136 # 'disable_inventory' => 1, #
139 # 'label' => 'Description',
140 # 'def_label' => 'Description for service defs',
141 # 'type' => 'select',
142 # 'select_table' => 'foreign_table',
143 # 'select_key' => 'key_field_in_table',
144 # 'select_label' => 'label_field_in_table',
153 Returns a meaningful identifier for this example
159 # $self->label_field; #or something more complicated if necessary
160 # check privatekey, check->privatekey, more?
161 return 'Certificate';
166 Adds this record to the database. If there is an error, returns the error,
167 otherwise returns false.
171 # the insert method can be inherited from FS::Record
175 Delete this record from the database.
179 # the delete method can be inherited from FS::Record
181 =item replace OLD_RECORD
183 Replaces the OLD_RECORD with this one in the database. If there is an error,
184 returns the error, otherwise returns false.
188 # the replace method can be inherited from FS::Record
192 Checks all fields to make sure this is a valid certificate. If there is
193 an error, returns the error, otherwise returns false. Called by the insert
198 # the check method should currently be supplied - FS::Record contains some
199 # data checking routines
205 $self->ut_numbern('svcnum')
206 || $self->ut_numbern('recnum')
207 || $self->ut_anything('privatekey') #XXX
208 || $self->ut_anything('csr') #XXX
209 || $self->ut_anything('certificate')#XXX
210 || $self->ut_anything('cacert') #XXX
211 || $self->ut_textn('common_name')
212 || $self->ut_textn('organization')
213 || $self->ut_textn('organization_unit')
214 || $self->ut_textn('city')
215 || $self->ut_textn('state')
216 || $self->ut_textn('country') #XXX char(2) or NULL
217 || $self->ut_textn('cert_contact')
219 return $error if $error;
224 =item generate_privatekey [ KEYSIZE ]
228 use IPC::Run qw( run );
231 sub generate_privatekey {
233 my $keysize = (@_ && $_[0]) ? shift : 2048;
234 run( [qw( openssl genrsa ), $keysize], '>pipe'=>\*OUT, '2>'=>'/dev/null' )
235 or die "error running openssl: $!";
237 my $privatekey = join('', <OUT>);
238 $self->privatekey($privatekey);
241 =item check_privatekey
245 sub check_privatekey {
247 my $in = $self->privatekey;
248 run( [qw( openssl rsa -check -noout)], '<'=>\$in, '>pipe'=>\*OUT, '2>'=>'/dev/null' )
249 ;# or die "error running openssl: $!";
252 return ($ok =~ /key ok/);
255 tie my %subj, 'Tie::IxHash',
256 'CN' => 'common_name',
257 'O' => 'organization',
258 'OU' => 'organization_unit',
271 '/'. join('/', map { my $v = $self->get($subj{$_});
272 $v =~ s/([=\/])/\\$1/;
282 my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; #XXX actual cache dir
283 my $fh = new File::Temp(
284 TEMPLATE => 'cert.'. '.XXXXXXXX',
286 ) or die "can't open temp file: $!\n";
287 print $fh $self->$field;
295 my $fh = $self->_file('privatekey');
297 run( [qw( openssl req -new -key ), $fh->filename, '-subj', $self->subj ],
298 '>pipe'=>\*OUT, '2>'=>'/dev/null'
300 or die "error running openssl: $!";
302 my $csr = join('', <OUT>);
311 run( [qw( openssl req -subject -noout ), ],
313 '>pipe'=>\*OUT, '2>'=>'/dev/null'
315 ;#or die "error running openssl: $!";
317 #subject=/CN=cn.example.com/ST=AK/O=Tofuy/OU=Soybean dept./C=US/L=Tofutown
319 $line =~ /^subject=\/(.*)$/ or return ();
322 map { if ( /^\s*(\w+)=\s*(.*)\s*$/ ) {
331 sub generate_selfsigned {
336 my $key = $self->_file('privatekey');
337 my $csr = $self->_file('csr');
339 run( [qw( openssl req -x509 -nodes ),
341 '-key' => $key->filename,
342 '-in' => $csr->filename,
344 '>pipe'=>\*OUT, '2>'=>'/dev/null'
346 or die "error running openssl: $!";
348 my $certificate = join('', <OUT>);
349 $self->certificate($certificate);
352 #openssl x509 -in cert -noout -subject -issuer -dates -serial
353 #subject= /CN=cn.example.com/ST=AK/O=Tofuy/OU=Soybean dept./C=US/L=Tofutown
354 #issuer= /CN=cn.example.com/ST=AK/O=Tofuy/OU=Soybean dept./C=US/L=Tofutown
355 #notBefore=Nov 7 05:07:42 2010 GMT
356 #notAfter=Nov 6 05:07:42 2012 GMT
357 #serial=B1DBF1A799EF207B
359 sub check_certificate { shift->check_x509('certificate'); }
360 sub check_cacert { shift->check_x509('cacert'); }
363 my( $self, $field ) = ( shift, shift );
365 my $in = $self->$field;
366 run( [qw( openssl x509 -noout -subject -issuer -dates -serial )],
368 '>pipe'=>\*OUT, '2>'=>'/dev/null'
370 or die "error running openssl: $!";
375 /^\s*(\w+)=\s*(.*)\s*$/ or next;
379 for my $f (qw( subject issuer )) {
381 $hash{$f} = { map { if ( /^\s*(\w+)=\s*(.*)\s*$/ ) {
387 split('/', $hash{$f})
392 $hash{'selfsigned'} = 1 if $hash{'subject'}->{'O'} eq $hash{'issuer'}->{'O'};
403 L<FS::Record>, schema.html from the base documentation.