Avoid calling globfree twice on failure.
[strongswan.git] / src / libcharon / plugins / stroke / stroke_cred.c
index e5718e5..f4616a2 100644 (file)
@@ -31,6 +31,7 @@
 #include <credentials/certificates/crl.h>
 #include <credentials/certificates/ac.h>
 #include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
 #include <utils/linked_list.h>
 #include <utils/lexparser.h>
 #include <threading/rwlock.h>
@@ -94,7 +95,8 @@ struct private_stroke_cred_t {
 typedef struct {
        private_stroke_cred_t *this;
        identification_t *id;
-       certificate_type_t type;
+       certificate_type_t cert;
+       key_type_t key;
 } id_data_t;
 
 /**
@@ -115,15 +117,18 @@ static bool private_filter(id_data_t *data,
        private_key_t *key;
 
        key = *in;
-       if (data->id == NULL)
+       if (data->key == KEY_ANY || data->key == key->get_type(key))
        {
-               *out = key;
-               return TRUE;
-       }
-       if (key->has_fingerprint(key, data->id->get_encoding(data->id)))
-       {
-               *out = key;
-               return TRUE;
+               if (data->id == NULL)
+               {
+                       *out = key;
+                       return TRUE;
+               }
+               if (key->has_fingerprint(key, data->id->get_encoding(data->id)))
+               {
+                       *out = key;
+                       return TRUE;
+               }
        }
        return FALSE;
 }
@@ -139,6 +144,7 @@ static enumerator_t* create_private_enumerator(private_stroke_cred_t *this,
        data = malloc_thing(id_data_t);
        data->this = this;
        data->id = id;
+       data->key = type;
 
        this->lock->read_lock(this->lock);
        return enumerator_create_filter(this->private->create_enumerator(this->private),
@@ -154,7 +160,7 @@ static bool certs_filter(id_data_t *data, certificate_t **in, certificate_t **ou
        public_key_t *public;
        certificate_t *cert = *in;
 
-       if (data->type != CERT_ANY && data->type != cert->get_type(cert))
+       if (data->cert != CERT_ANY && data->cert != cert->get_type(cert))
        {
                return FALSE;
        }
@@ -167,11 +173,14 @@ static bool certs_filter(id_data_t *data, certificate_t **in, certificate_t **ou
        public = cert->get_public_key(cert);
        if (public)
        {
-               if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
+               if (data->key == KEY_ANY || data->key != public->get_type(public))
                {
-                       public->destroy(public);
-                       *out = *in;
-                       return TRUE;
+                       if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
+                       {
+                               public->destroy(public);
+                               *out = *in;
+                               return TRUE;
+                       }
                }
                public->destroy(public);
        }
@@ -194,7 +203,8 @@ static enumerator_t* create_cert_enumerator(private_stroke_cred_t *this,
        data = malloc_thing(id_data_t);
        data->this = this;
        data->id = id;
-       data->type = cert;
+       data->cert = cert;
+       data->key = key;
 
        this->lock->read_lock(this->lock);
        return enumerator_create_filter(this->certs->create_enumerator(this->certs),
@@ -673,72 +683,125 @@ static err_t extract_secret(chunk_t *secret, chunk_t *line)
 }
 
 /**
- * Data to pass to passphrase_cb
+ * Data for passphrase callback
  */
 typedef struct {
        /** socket we use for prompting */
        FILE *prompt;
        /** private key file */
-       char *file;
-       /** buffer for passphrase */
-       char buf[256];
+       char *path;
+       /** number of tries */
+       int try;
 } passphrase_cb_data_t;
 
 /**
- * Passphrase callback to read from stroke fd
+ * Callback function to receive Passphrases
  */
-chunk_t passphrase_cb(passphrase_cb_data_t *data, int try)
+static shared_key_t* passphrase_cb(passphrase_cb_data_t *data,
+                                                               shared_key_type_t type,
+                                                               identification_t *me, identification_t *other,
+                                                               id_match_t *match_me, id_match_t *match_other)
 {
-       chunk_t secret = chunk_empty;;
+       chunk_t secret;
+       char buf[256];
 
-       if (try > 5)
-       {
-               fprintf(data->prompt, "invalid passphrase, too many trials\n");
-               return chunk_empty;
-       }
-       if (try == 1)
+       if (type != SHARED_ANY && type != SHARED_PRIVATE_KEY_PASS)
        {
-               fprintf(data->prompt, "Private key '%s' is encrypted\n", data->file);
+               return NULL;
        }
-       else
+
+       if (data->try > 1)
        {
-               fprintf(data->prompt, "invalid passphrase\n");
+               if (data->try > 5)
+               {
+                       fprintf(data->prompt, "PIN invalid, giving up.\n");
+                       return NULL;
+               }
+               fprintf(data->prompt, "PIN invalid!\n");
        }
+       data->try++;
+       fprintf(data->prompt, "Private key '%s' is encrypted.\n", data->path);
        fprintf(data->prompt, "Passphrase:\n");
-       if (fgets(data->buf, sizeof(data->buf), data->prompt))
+       if (fgets(buf, sizeof(buf), data->prompt))
        {
-               secret = chunk_create(data->buf, strlen(data->buf));
-               if (secret.len)
+               secret = chunk_create(buf, strlen(buf));
+               if (secret.len > 1)
                {       /* trim appended \n */
                        secret.len--;
+                       if (match_me)
+                       {
+                               *match_me = ID_MATCH_PERFECT;
+                       }
+                       if (match_other)
+                       {
+                               *match_other = ID_MATCH_NONE;
+                       }
+                       return shared_key_create(SHARED_PRIVATE_KEY_PASS, chunk_clone(secret));
                }
        }
-       return secret;
+       return NULL;
 }
 
 /**
- * Smartcard PIN callback to read from stroke fd
+ * Data for PIN callback
  */
-chunk_t smartcard_cb(passphrase_cb_data_t *data, int try)
+typedef struct {
+       /** socket we use for prompting */
+       FILE *prompt;
+       /** card label */
+       char *card;
+       /** card keyid */
+       chunk_t keyid;
+       /** number of tries */
+       int try;
+} pin_cb_data_t;
+
+/**
+ * Callback function to receive PINs
+ */
+static shared_key_t* pin_cb(pin_cb_data_t *data, shared_key_type_t type,
+                                                       identification_t *me, identification_t *other,
+                                                       id_match_t *match_me, id_match_t *match_other)
 {
-       chunk_t secret = chunk_empty;;
+       chunk_t secret;
+       char buf[256];
 
-       if (try != 1)
+       if (type != SHARED_ANY && type != SHARED_PIN)
        {
-               fprintf(data->prompt, "invalid passphrase, aborting\n");
-               return chunk_empty;
+               return NULL;
        }
-       fprintf(data->prompt, "Login to '%s' required\n", data->file);
-       fprintf(data->prompt, "Passphrase:\n");
-       if (fgets(data->buf, sizeof(data->buf), data->prompt))
+
+       if (!me || !chunk_equals(me->get_encoding(me), data->keyid))
        {
-               secret = chunk_create(data->buf, strlen(data->buf));
-               if (secret.len)
+               return NULL;
+       }
+
+       if (data->try > 1)
+       {
+               fprintf(data->prompt, "PIN invalid, aborting.\n");
+               return NULL;
+       }
+       data->try++;
+       fprintf(data->prompt, "Login to '%s' required\n", data->card);
+       fprintf(data->prompt, "PIN:\n");
+       if (fgets(buf, sizeof(buf), data->prompt))
+       {
+               secret = chunk_create(buf, strlen(buf));
+               if (secret.len > 1)
                {       /* trim appended \n */
                        secret.len--;
+                       if (match_me)
+                       {
+                               *match_me = ID_MATCH_PERFECT;
+                       }
+                       if (match_other)
+                       {
+                               *match_other = ID_MATCH_NONE;
+                       }
+                       return shared_key_create(SHARED_PIN, chunk_clone(secret));
                }
        }
-       return secret;
+       return NULL;
 }
 
 /**
@@ -754,8 +817,9 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
        chunk_t chunk;
        shared_key_t *shared;
        identification_t *id;
-       mem_cred_t *mem;
-       callback_set_t *cb;
+       mem_cred_t *mem = NULL;
+       callback_cred_t *cb = NULL;
+       pin_cb_data_t pin_data;
        enum {
                SC_FORMAT_SLOT_MODULE_KEYID,
                SC_FORMAT_SLOT_KEYID,
@@ -821,22 +885,34 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                DBG1(DBG_CFG, "line %d: malformed PIN: %s", line_nr, ugh);
                return FALSE;
        }
+
+       chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
        if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
        {
-               if (prompt)
-               {
-                       passphrase_cb_data_t data = {
-                               .prompt = prompt,
-                               .file = path,
-                       };
+               free(secret.ptr);
+               if (!prompt)
+               {       /* no IO channel to prompt, skip */
+                       free(chunk.ptr);
+                       return TRUE;
+               }
+               /* use callback credential set to prompt for the pin */
+               pin_data.prompt = prompt;
+               pin_data.card = smartcard;
+               pin_data.keyid = chunk;
+               pin_data.try = 1;
+               cb = callback_cred_create_shared((void*)pin_cb, &pin_data);
+               lib->credmgr->add_local_set(lib->credmgr, &cb->set);
+       }
+       else
+       {
+               /* provide our pin in a temporary credential set */
+               shared = shared_key_create(SHARED_PIN, secret);
+               id = identification_create_from_encoding(ID_KEY_ID, chunk);
+               mem = mem_cred_create();
+               mem->add_shared(mem, shared, id, NULL);
+               lib->credmgr->add_local_set(lib->credmgr, &mem->set);
+       }
 
-       /* provide our pin in a temporary credential set */
-       shared = shared_key_create(SHARED_PIN, secret);
-       chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
-       id = identification_create_from_encoding(ID_KEY_ID, chunk);
-       set = mem_cred_create();
-       set->add_shared(set, shared, id, NULL);
-       lib->credmgr->add_local_set(lib->credmgr, &set->set);
        /* unlock: smartcard needs the pin and potentially calls public set */
        this->lock->unlock(this->lock);
        switch (format)
@@ -861,8 +937,16 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                        break;
        }
        this->lock->write_lock(this->lock);
-       lib->credmgr->remove_local_set(lib->credmgr, &set->set);
-       set->destroy(set);
+       if (mem)
+       {
+               lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
+               mem->destroy(mem);
+       }
+       if (cb)
+       {
+               lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
+               cb->destroy(cb);
+       }
 
        if (key)
        {
@@ -881,7 +965,7 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
        char path[PATH_MAX];
        chunk_t filename;
        chunk_t secret = chunk_empty;
-       private_key_t *key = NULL;
+       private_key_t *key;
 
        err_t ugh = extract_value(&filename, &line);
 
@@ -919,23 +1003,53 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
        }
        if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
        {
-               if (prompt)
+               callback_cred_t *cb = NULL;
+               passphrase_cb_data_t pp_data = {
+                       .prompt = prompt,
+                       .path = path,
+                       .try = 1,
+               };
+
+               free(secret.ptr);
+               if (!prompt)
                {
-                       passphrase_cb_data_t data = {
-                               .prompt = prompt,
-                               .file = path,
-                       };
-                       key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
-                                                                        key_type, BUILD_FROM_FILE, path,
-                                                                        BUILD_PASSPHRASE_CALLBACK,
-                                                                        passphrase_cb, &data, BUILD_END);
+                       return TRUE;
                }
+               /* use callback credential set to prompt for the passphrase */
+               pp_data.prompt = prompt;
+               pp_data.path = path;
+               pp_data.try = 1;
+               cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data);
+               lib->credmgr->add_local_set(lib->credmgr, &cb->set);
+
+               /* unlock, as the builder might ask for a secret */
+               this->lock->unlock(this->lock);
+               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
+                                                                BUILD_FROM_FILE, path, BUILD_END);
+               this->lock->write_lock(this->lock);
+
+               lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
+               cb->destroy(cb);
        }
        else
        {
+               mem_cred_t *mem = NULL;
+               shared_key_t *shared;
+
+               /* provide our pin in a temporary credential set */
+               shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret);
+               mem = mem_cred_create();
+               mem->add_shared(mem, shared, NULL);
+               lib->credmgr->add_local_set(lib->credmgr, &mem->set);
+
+               /* unlock, as the builder might ask for a secret */
+               this->lock->unlock(this->lock);
                key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
-                                                                BUILD_FROM_FILE, path,
-                                                                BUILD_PASSPHRASE, secret, BUILD_END);
+                                                                BUILD_FROM_FILE, path, BUILD_END);
+               this->lock->write_lock(this->lock);
+
+               lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
+               mem->destroy(mem);
        }
        if (key)
        {
@@ -947,7 +1061,6 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
        {
                DBG1(DBG_CFG, "  loading private key from '%s' failed", path);
        }
-       chunk_clear(&secret);
        return TRUE;
 }
 
@@ -1037,7 +1150,7 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                close(fd);
                return;
        }
-       addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       addr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (addr == MAP_FAILED)
        {
                DBG1(DBG_LIB, "mapping '%s' failed: %s", file, strerror(errno));
@@ -1121,7 +1234,6 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                        if (glob(pattern, GLOB_ERR, NULL, &buf) != 0)
                        {
                                DBG1(DBG_CFG, "expanding file expression '%s' failed", pattern);
-                               globfree(&buf);
                        }
                        else
                        {