~statik/ubuntu/maverick/erlang/erlang-merge-testing

1.2.5 by Sergei Golovan
Import upstream version 13.b.4-dfsg
1
%%
2
%% %CopyrightBegin%
3
%% 
4
%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
5
%% 
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
11
%% 
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
15
%% under the License.
16
%% 
17
%% %CopyrightEnd%
18
%%
19
20
-module(make_certs).
21
22
-export([all/2]).
23
24
-record(dn, {commonName, 
25
	     organizationalUnitName = "Erlang OTP",
26
	     organizationName = "Ericsson AB",
27
	     localityName = "Stockholm",
28
	     countryName = "SE",
29
	     emailAddress = "peter@erix.ericsson.se"}).
30
31
all(DataDir, PrivDir) ->
32
    OpenSSLCmd = "openssl",
33
    create_rnd(DataDir, PrivDir),			% For all requests
34
    rootCA(PrivDir, OpenSSLCmd, "erlangCA"),
35
    intermediateCA(PrivDir, OpenSSLCmd, "otpCA", "erlangCA"),
36
    endusers(PrivDir, OpenSSLCmd, "otpCA", ["client", "server"]),
37
    collect_certs(PrivDir, ["erlangCA", "otpCA"], ["client", "server"]),
38
    %% Create keycert files 
39
    SDir = filename:join([PrivDir, "server"]),
40
    SC = filename:join([SDir, "cert.pem"]),
41
    SK = filename:join([SDir, "key.pem"]),
42
    SKC = filename:join([SDir, "keycert.pem"]),
43
    append_files([SK, SC], SKC),
44
    CDir = filename:join([PrivDir, "client"]),
45
    CC = filename:join([CDir, "cert.pem"]),
46
    CK = filename:join([CDir, "key.pem"]),
47
    CKC = filename:join([CDir, "keycert.pem"]),
48
    append_files([CK, CC], CKC),
49
    remove_rnd(PrivDir).
50
51
append_files(FileNames, ResultFileName) ->
52
    {ok, ResultFile} = file:open(ResultFileName, [write]),
53
    do_append_files(FileNames, ResultFile).
54
55
do_append_files([], RF) ->
56
    ok = file:close(RF);
57
do_append_files([F|Fs], RF) ->
58
    {ok, Data} = file:read_file(F),
59
    ok = file:write(RF, Data),
60
    do_append_files(Fs, RF).
61
62
rootCA(Root, OpenSSLCmd, Name) ->
63
    create_ca_dir(Root, Name, ca_cnf(Name)),
64
    DN = #dn{commonName = Name},
65
    create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
66
    ok.
67
68
intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
69
    CA = "otpCA", 
70
    create_ca_dir(Root, CA, ca_cnf(CA)),
71
    CARoot = filename:join([Root, CA]),
72
    DN = #dn{commonName = CA},
73
    CnfFile = filename:join([CARoot, "req.cnf"]),
74
    file:write_file(CnfFile, req_cnf(DN)),
75
    KeyFile = filename:join([CARoot, "private", "key.pem"]), 
76
    ReqFile =  filename:join([CARoot, "req.pem"]), 
77
    create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
78
    CertFile = filename:join([CARoot, "cert.pem"]),
79
    sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
80
81
endusers(Root, OpenSSLCmd, CA, Users) ->
82
    lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
83
84
enduser(Root, OpenSSLCmd, CA, User) -> 
85
    UsrRoot = filename:join([Root, User]),
86
    file:make_dir(UsrRoot),
87
    CnfFile = filename:join([UsrRoot, "req.cnf"]),
88
    DN = #dn{commonName = User},
89
    file:write_file(CnfFile, req_cnf(DN)),
90
    KeyFile = filename:join([UsrRoot, "key.pem"]), 
91
    ReqFile =  filename:join([UsrRoot, "req.pem"]), 
92
    create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
93
    CertFile =  filename:join([UsrRoot, "cert.pem"]), 
94
    sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile).
95
96
collect_certs(Root, CAs, Users) ->
97
    Bins = lists:foldr(
98
	     fun(CA, Acc) -> 
99
		     File = filename:join([Root, CA, "cert.pem"]),
100
		     {ok, Bin} = file:read_file(File),
101
		     [Bin, "\n" | Acc]
102
	     end, [], CAs),
103
    lists:foreach(
104
      fun(User) ->
105
	      File = filename:join([Root, User, "cacerts.pem"]),
106
	      file:write_file(File, Bins)
107
      end, Users).
108
109
create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
110
    CARoot = filename:join([Root, CAName]),
111
    CnfFile = filename:join([CARoot, "req.cnf"]),
112
    file:write_file(CnfFile, Cnf),
113
    KeyFile = filename:join([CARoot, "private", "key.pem"]), 
114
    CertFile = filename:join([CARoot, "cert.pem"]), 
115
    Cmd = [OpenSSLCmd, " req"
116
	   " -new"
117
	   " -x509"
118
	   " -config ", CnfFile,
119
	   " -keyout ", KeyFile, 
120
	   " -out ", CertFile], 
121
    Env = [{"ROOTDIR", Root}],  
122
    cmd(Cmd, Env).
123
124
create_ca_dir(Root, CAName, Cnf) ->
125
    CARoot = filename:join([Root, CAName]),
126
    file:make_dir(CARoot),
127
    create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
128
    create_rnd(Root, filename:join([CAName, "private"])),
129
    create_files(CARoot, [{"serial", "01\n"},
130
			  {"index.txt", ""},
131
			  {"ca.cnf", Cnf}]).
132
133
create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
134
    Cmd = [OpenSSLCmd, " req"
135
	   " -new"
136
	   " -config ", CnfFile,
137
	   " -keyout ", KeyFile, 
138
	   " -out ", ReqFile], 
139
    Env = [{"ROOTDIR", Root}], 
140
    cmd(Cmd, Env).
141
142
sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
143
    CACnfFile = filename:join([Root, CA, "ca.cnf"]),
144
    Cmd = [OpenSSLCmd, " ca"
145
	   " -batch"
146
	   " -notext"
147
	   " -config ", CACnfFile, 
148
	   " -extensions ", CertType,
149
	   " -in ", ReqFile, 
150
	   " -out ", CertFile],
151
    Env = [{"ROOTDIR", Root}], 
152
    cmd(Cmd, Env).
153
    
154
%%
155
%%  Misc
156
%%
157
    
158
create_dirs(Root, Dirs) ->
159
    lists:foreach(fun(Dir) ->
160
			  file:make_dir(filename:join([Root, Dir])) end,
161
		  Dirs).
162
163
create_files(Root, NameContents) ->
164
    lists:foreach(
165
      fun({Name, Contents}) ->
166
	      file:write_file(filename:join([Root, Name]), Contents) end,
167
      NameContents).
168
169
create_rnd(FromDir, ToDir) ->
170
     From = filename:join([FromDir, "RAND"]),
171
     To = filename:join([ToDir, "RAND"]),
172
     file:copy(From, To).
173
174
remove_rnd(Dir) ->
175
    File = filename:join([Dir, "RAND"]),
176
    file:delete(File).
177
178
cmd(Cmd, Env) ->
179
    FCmd = lists:flatten(Cmd),
180
    Port = open_port({spawn, FCmd}, [stream, eof, exit_status, stderr_to_stdout, 
181
				    {env, Env}]),
182
    eval_cmd(Port).
183
184
eval_cmd(Port) ->
185
    receive 
186
	{Port, {data, _}} ->
187
	    eval_cmd(Port);
188
	{Port, eof} ->
189
	    ok
190
    end,
191
    receive
192
	{Port, {exit_status, Status}} when Status /= 0 ->
193
	    %% io:fwrite("exit status: ~w~n", [Status]),
194
	    exit({eval_cmd, Status})
195
    after 0 ->
196
	    ok
197
    end.
198
199
%%
200
%% Contents of configuration files 
201
%%
202
203
req_cnf(DN) ->
204
    ["# Purpose: Configuration for requests (end users and CAs)."
205
     "\n"
206
     "ROOTDIR	        = $ENV::ROOTDIR\n"
207
     "\n"
208
209
     "[req]\n"
210
     "input_password	= secret\n"
211
     "output_password	= secret\n"
212
     "default_bits	= 1024\n"
213
     "RANDFILE		= $ROOTDIR/RAND\n"
214
     "encrypt_key	= no\n"
215
     "default_md	= sha1\n"
216
     "#string_mask	= pkix\n"
217
     "x509_extensions	= ca_ext\n"
218
     "prompt		= no\n"
219
     "distinguished_name= name\n"
220
     "\n"
221
222
     "[name]\n"
223
     "commonName		= ", DN#dn.commonName, "\n"
224
     "organizationalUnitName	= ", DN#dn.organizationalUnitName, "\n"
225
     "organizationName	        = ", DN#dn.organizationName, "\n" 
226
     "localityName		= ", DN#dn.localityName, "\n"
227
     "countryName		= ", DN#dn.countryName, "\n"
228
     "emailAddress		= ", DN#dn.emailAddress, "\n"
229
     "\n"
230
231
     "[ca_ext]\n"
232
     "basicConstraints 	= critical, CA:true\n"
233
     "keyUsage 		= cRLSign, keyCertSign\n"
234
     "subjectKeyIdentifier = hash\n"
235
     "subjectAltName	= email:copy\n"].
236
237
238
ca_cnf(CA) ->
239
    ["# Purpose: Configuration for CAs.\n"
240
     "\n"
241
     "ROOTDIR	        = $ENV::ROOTDIR\n"
242
     "default_ca	= ca\n"
243
     "\n"
244
245
     "[ca]\n"
246
     "dir		= $ROOTDIR/", CA, "\n"
247
     "certs		= $dir/certs\n"
248
     "crl_dir	        = $dir/crl\n"
249
     "database	        = $dir/index.txt\n"
250
     "new_certs_dir	= $dir/newcerts\n"
251
     "certificate	= $dir/cert.pem\n"
252
     "serial		= $dir/serial\n"
253
     "crl		= $dir/crl.pem\n"
254
     "private_key	= $dir/private/key.pem\n"
255
     "RANDFILE	        = $dir/private/RAND\n"
256
     "\n"
257
     "x509_extensions   = user_cert\n"
258
     "default_days	= 3600\n"
259
     "default_md	= sha1\n"
260
     "preserve	        = no\n"
261
     "policy		= policy_match\n"
262
     "\n"
263
264
     "[policy_match]\n"
265
     "commonName		= supplied\n"
266
     "organizationalUnitName	= optional\n"
267
     "organizationName	        = match\n"
268
     "countryName		= match\n"
269
     "localityName		= match\n"
270
     "emailAddress		= supplied\n"
271
     "\n"
272
273
     "[user_cert]\n"
274
     "basicConstraints	= CA:false\n"
275
     "keyUsage 		= nonRepudiation, digitalSignature, keyEncipherment\n"
276
     "subjectKeyIdentifier = hash\n"
277
     "authorityKeyIdentifier = keyid,issuer:always\n"
278
     "subjectAltName	= email:copy\n"
279
     "issuerAltName	= issuer:copy\n"
280
     "\n"
281
282
     "[ca_cert]\n"
283
     "basicConstraints 	= critical,CA:true\n"
284
     "keyUsage 		= cRLSign, keyCertSign\n"
285
     "subjectKeyIdentifier = hash\n"
286
     "authorityKeyIdentifier = keyid:always,issuer:always\n"
287
     "subjectAltName	= email:copy\n"
288
     "issuerAltName	= issuer:copy\n"].