iscsi tools: handle compile warnings about unused variables
[open-iscsi.git] / utils / open-isns / pki.c
blobf3af9220d58cf4533313915106b5b41a428b2ef4
1 /*
2 * PKI related functions
4 * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
5 */
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <limits.h>
11 #include <openssl/pem.h>
12 #include <openssl/err.h>
13 #include <fcntl.h>
14 #include "isns.h"
15 #include "security.h"
16 #include "util.h"
17 #include "config.h"
19 #ifdef WITH_SECURITY
21 /* versions prior to 9.6.8 didn't seem to have these */
22 #if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906080L
23 # define EVP_MD_CTX_init(c) do { } while (0)
24 # define EVP_MD_CTX_cleanup(c) do { } while (0)
25 #endif
26 #if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
27 # define i2d_DSA_PUBKEY i2d_DSA_PUBKEY_backwards
29 static int i2d_DSA_PUBKEY_backwards(DSA *, unsigned char **);
30 #endif
32 static int isns_openssl_init = 0;
34 static int isns_dsasig_verify(isns_security_t *ctx,
35 isns_principal_t *peer,
36 buf_t *pdu,
37 const struct isns_authblk *);
38 static int isns_dsasig_sign(isns_security_t *ctx,
39 isns_principal_t *peer,
40 buf_t *pdu,
41 struct isns_authblk *);
42 static EVP_PKEY *isns_dsasig_load_private_pem(isns_security_t *ctx,
43 const char *filename);
44 static EVP_PKEY *isns_dsasig_load_public_pem(isns_security_t *ctx,
45 const char *filename);
46 static DSA * isns_dsa_load_params(const char *);
50 * Create a DSA security context
52 isns_security_t *
53 isns_create_dsa_context(void)
55 isns_security_t *ctx;
57 if (!isns_openssl_init) {
58 ERR_load_crypto_strings();
59 OpenSSL_add_all_algorithms();
60 OpenSSL_add_all_ciphers();
61 OpenSSL_add_all_digests();
62 isns_openssl_init = 1;
65 ctx = isns_calloc(1, sizeof(*ctx));
67 ctx->is_name = "DSA";
68 ctx->is_type = ISNS_AUTH_TYPE_SHA1_DSA;
69 ctx->is_replay_window = isns_config.ic_auth.replay_window;
70 ctx->is_timestamp_jitter = isns_config.ic_auth.timestamp_jitter;
72 ctx->is_verify = isns_dsasig_verify;
73 ctx->is_sign = isns_dsasig_sign;
74 ctx->is_load_private = isns_dsasig_load_private_pem;
75 ctx->is_load_public = isns_dsasig_load_public_pem;
77 isns_debug_auth("Created DSA authentication context\n");
78 return ctx;
82 * DSA signature generation and verification
84 static void
85 isns_message_digest(EVP_MD_CTX *md, const buf_t *pdu,
86 const struct isns_authblk *blk)
88 uint64_t stamp;
90 EVP_DigestUpdate(md, buf_head(pdu), buf_avail(pdu));
92 /* The RFC doesn't say which pieces of the
93 * message should be hashed.
94 * We make an educated guess.
96 stamp = htonll(blk->iab_timestamp);
97 EVP_DigestUpdate(md, &stamp, sizeof(stamp));
100 static void
101 isns_dsasig_report_errors(const char *msg, isns_print_fn_t *fn)
103 unsigned long code;
105 fn("%s - OpenSSL errors follow:\n", msg);
106 while ((code = ERR_get_error()) != 0)
107 fn("> %s: %s\n",
108 ERR_func_error_string(code),
109 ERR_reason_error_string(code));
113 isns_dsasig_sign(isns_security_t *ctx,
114 isns_principal_t *peer,
115 buf_t *pdu,
116 struct isns_authblk *blk)
118 static unsigned char signature[1024];
119 unsigned int sig_len = sizeof(signature);
120 EVP_MD_CTX md_ctx;
121 EVP_PKEY *pkey;
122 int err;
124 if ((pkey = peer->is_key) == NULL)
125 return 0;
127 if (pkey->type != EVP_PKEY_DSA) {
128 isns_debug_message(
129 "Incompatible public key (spi=%s)\n",
130 peer->is_name);
131 return 0;
133 if (EVP_PKEY_size(pkey) > sizeof(signature)) {
134 isns_error("isns_dsasig_sign: signature buffer too small\n");
135 return 0;
137 if (pkey->pkey.dsa->priv_key == NULL) {
138 isns_error("isns_dsasig_sign: oops, seems to be a public key\n");
139 return 0;
142 isns_debug_auth("Signing messages with spi=%s, DSA/%u\n",
143 peer->is_name, EVP_PKEY_bits(pkey));
145 EVP_MD_CTX_init(&md_ctx);
146 EVP_SignInit(&md_ctx, EVP_dss1());
147 isns_message_digest(&md_ctx, pdu, blk);
148 err = EVP_SignFinal(&md_ctx,
149 signature, &sig_len,
150 pkey);
151 EVP_MD_CTX_cleanup(&md_ctx);
153 if (err == 0) {
154 isns_dsasig_report_errors("EVP_SignFinal failed", isns_error);
155 return 0;
158 blk->iab_sig = signature;
159 blk->iab_sig_len = sig_len;
160 return 1;
164 isns_dsasig_verify(isns_security_t *ctx,
165 isns_principal_t *peer,
166 buf_t *pdu,
167 const struct isns_authblk *blk)
169 EVP_MD_CTX md_ctx;
170 EVP_PKEY *pkey;
171 int err;
173 if ((pkey = peer->is_key) == NULL)
174 return 0;
176 if (pkey->type != EVP_PKEY_DSA) {
177 isns_debug_message(
178 "Incompatible public key (spi=%s)\n",
179 peer->is_name);
180 return 0;
183 EVP_MD_CTX_init(&md_ctx);
184 EVP_VerifyInit(&md_ctx, EVP_dss1());
185 isns_message_digest(&md_ctx, pdu, blk);
186 err = EVP_VerifyFinal(&md_ctx,
187 blk->iab_sig, blk->iab_sig_len,
188 pkey);
189 EVP_MD_CTX_cleanup(&md_ctx);
191 if (err == 0) {
192 isns_debug_auth("*** Incorrect signature ***\n");
193 return 0;
195 if (err < 0) {
196 isns_dsasig_report_errors("EVP_VerifyFinal failed", isns_error);
197 return 0;
200 isns_debug_message("Good signature from %s\n",
201 peer->is_name?: "<server>");
202 return 1;
205 EVP_PKEY *
206 isns_dsasig_load_private_pem(isns_security_t *ctx, const char *filename)
208 EVP_PKEY *pkey;
209 FILE *fp;
211 if (!(fp = fopen(filename, "r"))) {
212 isns_error("Unable to open DSA keyfile %s: %m\n",
213 filename);
214 return 0;
217 pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
218 fclose(fp);
219 return pkey;
222 EVP_PKEY *
223 isns_dsasig_load_public_pem(isns_security_t *ctx, const char *filename)
225 EVP_PKEY *pkey;
226 FILE *fp;
228 if (!(fp = fopen(filename, "r"))) {
229 isns_error("Unable to open DSA keyfile %s: %m\n",
230 filename);
231 return 0;
234 pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
235 if (pkey == NULL) {
236 isns_dsasig_report_errors("Error loading DSA public key",
237 isns_error);
240 fclose(fp);
241 return pkey;
244 EVP_PKEY *
245 isns_dsa_decode_public(const void *ptr, size_t len)
247 const unsigned char *der = ptr;
248 EVP_PKEY *evp;
249 DSA *dsa;
251 /* Assigning ptr to a temporary variable avoids a silly
252 * compiled warning about type-punning. */
253 dsa = d2i_DSA_PUBKEY(NULL, &der, len);
254 if (dsa == NULL)
255 return NULL;
257 evp = EVP_PKEY_new();
258 EVP_PKEY_assign_DSA(evp, dsa);
259 return evp;
263 isns_dsa_encode_public(EVP_PKEY *pkey, void **ptr, size_t *len)
265 int bytes;
267 *ptr = NULL;
268 bytes = i2d_DSA_PUBKEY(pkey->pkey.dsa, (unsigned char **) ptr);
269 if (bytes < 0)
270 return 0;
272 *len = bytes;
273 return 1;
276 EVP_PKEY *
277 isns_dsa_load_public(const char *name)
279 return isns_dsasig_load_public_pem(NULL, name);
283 isns_dsa_store_private(const char *name, EVP_PKEY *key)
285 FILE *fp;
286 int rv, fd;
288 if ((fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
289 isns_error("Cannot save DSA key to %s: %m\n", name);
290 return 0;
293 if (!(fp = fdopen(fd, "w"))) {
294 isns_error("fdopen(%s): %m\n", name);
295 close(fd);
296 return 0;
299 rv = PEM_write_PrivateKey(fp, key, NULL, NULL, 0, 0, NULL);
300 fclose(fp);
302 if (rv == 0)
303 isns_dsasig_report_errors("Failed to store private key",
304 isns_error);
306 return rv;
310 isns_dsa_store_public(const char *name, EVP_PKEY *key)
312 FILE *fp;
313 int rv;
315 if (!(fp = fopen(name, "w"))) {
316 isns_error("Unable to open %s: %m\n", name);
317 return 0;
320 rv = PEM_write_PUBKEY(fp, key);
321 fclose(fp);
323 if (rv == 0)
324 isns_dsasig_report_errors("Failed to store public key",
325 isns_error);
327 return rv;
332 * DSA key generation
334 EVP_PKEY *
335 isns_dsa_generate_key(void)
337 EVP_PKEY *pkey;
338 DSA *dsa = NULL;
340 if (!(dsa = isns_dsa_load_params(isns_config.ic_dsa.param_file)))
341 goto failed;
343 if (!DSA_generate_key(dsa)) {
344 isns_dsasig_report_errors("Failed to generate DSA key",
345 isns_error);
346 goto failed;
349 pkey = EVP_PKEY_new();
350 EVP_PKEY_assign_DSA(pkey, dsa);
351 return pkey;
353 failed:
354 if (dsa)
355 DSA_free(dsa);
356 return NULL;
359 DSA *
360 isns_dsa_load_params(const char *filename)
362 FILE *fp;
363 DSA *dsa;
365 if (!filename) {
366 isns_error("Cannot generate key - no DSA parameter file\n");
367 return NULL;
369 if (!(fp = fopen(filename, "r"))) {
370 isns_error("Unable to open %s: %m\n", filename);
371 return NULL;
374 dsa = PEM_read_DSAparams(fp, NULL, NULL, NULL);
375 fclose(fp);
377 if (dsa == NULL) {
378 isns_dsasig_report_errors("Error loading DSA parameters",
379 isns_error);
382 return dsa;
385 static void
386 isns_dsa_param_gen_callback(int stage, int index, void *dummy)
388 if (stage == 0)
389 write(1, "+", 1);
390 else if (stage == 1)
391 write(1, ".", 1);
392 else if (stage == 2)
393 write(1, "/", 1);
397 isns_dsa_init_params(const char *filename)
399 FILE *fp;
400 DSA *dsa;
402 if (access(filename, R_OK) == 0)
403 return 1;
405 isns_mkdir_recursive(isns_dirname(filename));
406 if (!(fp = fopen(filename, "w"))) {
407 isns_error("Unable to open %s: %m\n", filename);
408 return 0;
411 isns_notice("Generating DSA parameters; this may take a while\n");
412 dsa = DSA_generate_parameters(1024, NULL, 0,
413 NULL, NULL, isns_dsa_param_gen_callback, NULL);
414 write(1, "\n", 1);
416 if (dsa == NULL) {
417 isns_dsasig_report_errors("Error generating DSA parameters",
418 isns_error);
419 fclose(fp);
420 return 0;
423 if (!PEM_write_DSAparams(fp, dsa)) {
424 isns_dsasig_report_errors("Error writing DSA parameters",
425 isns_error);
426 DSA_free(dsa);
427 fclose(fp);
428 return 0;
430 DSA_free(dsa);
431 fclose(fp);
432 return 1;
436 * Make sure the authentication key is present.
439 isns_dsa_init_key(const char *filename)
441 char pubkey_path[1024];
442 EVP_PKEY *pkey;
444 isns_mkdir_recursive(isns_dirname(filename));
445 snprintf(pubkey_path, sizeof(pubkey_path),
446 "%s.pub", filename);
447 if (access(filename, R_OK) == 0
448 && access(pubkey_path, R_OK) == 0)
449 return 1;
451 if (!(pkey = isns_dsa_generate_key())) {
452 isns_error("Failed to generate AuthKey\n");
453 return 0;
456 if (!isns_dsa_store_private(filename, pkey)) {
457 isns_error("Unable to write private key to %s\n", filename);
458 return 0;
460 isns_notice("Stored private key in %s\n", filename);
462 if (!isns_dsa_store_public(pubkey_path, pkey)) {
463 isns_error("Unable to write public key to %s\n", pubkey_path);
464 return 0;
466 isns_notice("Stored private key in %s\n", pubkey_path);
468 return 1;
472 * Simple keystore - this is a flat directory, with
473 * public key files using the SPI as their name.
475 typedef struct isns_simple_keystore isns_simple_keystore_t;
476 struct isns_simple_keystore {
477 isns_keystore_t sc_base;
478 char * sc_dirpath;
482 * Load a DSA key from the cert store
483 * In fact, this will load RSA keys as well.
485 static EVP_PKEY *
486 __isns_simple_keystore_find(isns_keystore_t *store_base,
487 const char *name, size_t namelen)
489 isns_simple_keystore_t *store = (isns_simple_keystore_t *) store_base;
490 char pathname[PATH_MAX];
492 /* Refuse to open key files with names
493 * that refer to parent directories */
494 if (memchr(name, '/', namelen) || name[0] == '.')
495 return NULL;
497 snprintf(pathname, sizeof(pathname),
498 "%s/%.*s", store->sc_dirpath,
499 (int) namelen, name);
500 if (access(pathname, R_OK) < 0)
501 return NULL;
502 return isns_dsasig_load_public_pem(NULL, pathname);
505 isns_keystore_t *
506 isns_create_simple_keystore(const char *dirname)
508 isns_simple_keystore_t *store;
510 store = isns_calloc(1, sizeof(*store));
511 store->sc_base.ic_name = "simple key store";
512 store->sc_base.ic_find = __isns_simple_keystore_find;
513 store->sc_dirpath = isns_strdup(dirname);
515 return (isns_keystore_t *) store;
518 #if OPADDRCONFIGENSSL_VERSION_NUMBER < 0x00906070L
519 #undef i2d_DSA_PUBKEY
522 i2d_DSA_PUBKEY_backwards(DSA *dsa, unsigned char **ptr)
524 unsigned char *buf;
525 int len;
527 len = i2d_DSA_PUBKEY(dsa, NULL);
528 if (len < 0)
529 return 0;
531 *ptr = buf = OPENSSL_malloc(len);
532 return i2d_DSA_PUBKEY(dsa, &buf);
534 #endif
536 #endif /* WITH_SECURITY */