summaryrefslogtreecommitdiff
path: root/tools/genssl
blob: 9c6cc3804ec2e1c65e696c4c6876832779fa3e50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env perl
#
# InspIRCd -- Internet Relay Chat Daemon
#
#   Copyright (C) 2013-2017 Sadie Powell <sadie@witchery.services>
#
# This file is part of InspIRCd.  InspIRCd is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


BEGIN {
	require 5.10.0;
}

use feature ':5.10';
use strict;
use warnings FATAL => qw(all);

use File::Temp();

# IMPORTANT: This script has to be able to run by itself so that it can be used
#            by binary distributions where the make/console.pm module will not
#            be available!

sub prompt($$) {
	my ($question, $default) = @_;
	return prompt_string(1, $question, $default) if eval 'use FindBin;use lib $FindBin::RealDir;use make::console; 1';
	say $question;
	print "[$default] => ";
	chomp(my $answer = <STDIN>);
	say '';
	return $answer ? $answer : $default;
}

if ($#ARGV != 0 || $ARGV[0] !~ /^(?:auto|gnutls|openssl)$/i) {
	say STDERR "Usage: $0 <auto|gnutls|openssl>";
	exit 1;
}

# On OS X the GnuTLS certtool is prefixed to avoid collision with the system certtool.
my $certtool = $^O eq 'darwin' ? 'gnutls-certtool' : 'certtool';

# Check whether the user has the required tools installed.
my $has_gnutls = `$certtool --version v 2>/dev/null`;
my $has_openssl = !system 'openssl version >/dev/null 2>&1';

# The framework the user has specified.
my $tool = lc $ARGV[0];

# If the user has not explicitly specified a framework then detect one.
if ($tool eq 'auto') {
	if ($has_gnutls) {
		$tool = 'gnutls';
	} elsif ($has_openssl) {
		$tool = 'openssl';
	} else {
		say STDERR "SSL generation failed: could not find $certtool or openssl in the PATH!";
		exit 1;
	}
} elsif ($tool eq 'gnutls' && !$has_gnutls) {
	say STDERR "SSL generation failed: could not find '$certtool' in the PATH!";
	exit 1;
} elsif ($tool eq 'openssl' && !$has_openssl) {
	say STDERR 'SSL generation failed: could not find \'openssl\' in the PATH!';
	exit 1;
}

# Harvest information needed to generate the certificate.
my $common_name = prompt('What is the hostname of your server?', 'irc.example.com');
my $email = prompt('What email address can you be contacted at?', 'example@example.com');
my $unit = prompt('What is the name of your unit?', 'Server Admins');
my $organization = prompt('What is the name of your organization?', 'Example IRC Network');
my $city = prompt('What city are you located in?', 'Example City');
my $state = prompt('What state are you located in?', 'Example State');
my $country = prompt('What is the ISO 3166-1 code for the country you are located in?', 'XZ');
my $days = prompt('How many days do you want your certificate to be valid for?', '365');

# Contains the SSL certificate in DER form.
my $dercert;

# Contains the exit code of openssl/gnutls-certtool.
my $status = 0;

if ($tool eq 'gnutls') {
	$has_gnutls =~ /certtool.+?(\d+\.\d+)/;
	my $sec_param = $1 lt '2.10' ? '--bits 2048' : '--sec-param normal';
	my $tmp = new File::Temp();
	print $tmp <<__GNUTLS_END__;
cn              = "$common_name"
email           = "$email"
unit            = "$unit"
organization    = "$organization"
locality        = "$city"
state           = "$state"
country         = "$country"
expiration_days = $days
tls_www_client
tls_www_server
signing_key
encryption_key
cert_signing_key
crl_signing_key
code_signing_key
ocsp_signing_key
time_stamping_key
__GNUTLS_END__
	close($tmp);
	$status ||= system "$certtool --generate-privkey $sec_param --outfile key.pem";
	$status ||= system "$certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem --template $tmp";
	$status ||= system "$certtool --generate-request --load-privkey key.pem --outfile csr.pem --template $tmp";
	$status ||= system "$certtool --generate-dh-params $sec_param --outfile dhparams.pem";
	$dercert = `$certtool --certificate-info --infile cert.pem --outder` unless $status;
} elsif ($tool eq 'openssl') {
	my $tmp = new File::Temp();
	print $tmp <<__OPENSSL_END__;
$country
$state
$city
$organization
$unit
$common_name
$email
.
$organization
__OPENSSL_END__
	close($tmp);
	$status ||= system "cat $tmp | openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days $days 2>/dev/null";
	$status ||= system "cat $tmp | openssl req -new -nodes -key key.pem -out csr.pem 2>/dev/null";
	$status ||= system 'openssl dhparam -out dhparams.pem 2048';
	$dercert = `openssl x509 -in cert.pem -outform DER` unless $status;
}

if ($status) {
	say STDERR "SSL generation failed: $tool exited with a non-zero status!";
	exit 1;
}

if (defined $dercert && eval 'use Digest::SHA; 1') {
	my $hash = Digest::SHA->new(256);
	$hash->add($dercert);
	say '';
	say 'If you are using the self-signed certificate then add this TLSA record to your domain for DANE support:';
	say "_6697._tcp." . $common_name . " TLSA 3 0 1 " . $hash->hexdigest;
}