]> granicus.if.org Git - neomutt/commitdiff
Rewrite S/MIME key searching. Add purpose checking.
authorKevin McCarthy <kevin@8t8.us>
Sun, 7 Jun 2015 20:59:32 +0000 (13:59 -0700)
committerKevin McCarthy <kevin@8t8.us>
Sun, 7 Jun 2015 20:59:32 +0000 (13:59 -0700)
Model the smime.c searching off of classic pgp searching.  Create
smime_get_key_by_hash()/addr()/str() functions that use a single
smime_get_candidates() function to search the index.

Use the new smime_keys generated purpose flag in the index to filter by
KEYFLAG_CANENCRYPT and KEYFLAG_CANSIGN.  Old style indices fall back to
all-inclusive behavior.

smime.c
smime.h

diff --git a/smime.c b/smime.c
index 378a1f0b5b1e381fc86db79819fb507e4964be2e..5ea66c9a7505181978167b979fb995314850ded6 100644 (file)
--- a/smime.c
+++ b/smime.c
@@ -63,16 +63,6 @@ struct smime_command_context {
 };
 
 
-typedef struct {
-  unsigned int hash;
-  char suffix;
-  char email[256];
-  char nick[256];
-  char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
-  short public; /* 1=public 0=private */
-} smime_id;
-
-
 char SmimePass[STRING];
 time_t SmimeExptime = 0; /* when does the cached passphrase expire? */
 
@@ -82,6 +72,45 @@ static char SmimeCertToUse[_POSIX_PATH_MAX];
 static char SmimeIntermediateToUse[_POSIX_PATH_MAX];
 
 
+void smime_free_key (smime_key_t **keylist)
+{
+  smime_key_t *key;
+
+  if (!keylist)
+    return;
+
+  while (*keylist)
+  {
+    key = *keylist;
+    *keylist = (*keylist)->next;
+
+    FREE (&key->email);
+    FREE (&key->hash);
+    FREE (&key->label);
+    FREE (&key->issuer);
+    FREE (&key);
+  }
+}
+
+static smime_key_t *smime_copy_key (smime_key_t *key)
+{
+  smime_key_t *copy;
+
+  if (!key)
+    return NULL;
+
+  copy = safe_calloc (sizeof (smime_key_t), 1);
+  copy->email  = safe_strdup(key->email);
+  copy->hash   = safe_strdup(key->hash);
+  copy->label  = safe_strdup(key->label);
+  copy->issuer = safe_strdup(key->issuer);
+  copy->trust  = key->trust;
+  copy->flags  = key->flags;
+
+  return copy;
+}
+
+
 /*
  *     Queries and passphrase handling.
  */
@@ -306,18 +335,32 @@ static pid_t smime_invoke (FILE **smimein, FILE **smimeout, FILE **smimeerr,
  */
 
 
+static char *smime_key_flags (int flags)
+{
+  static char buff[3];
+
+  if (!(flags & KEYFLAG_CANENCRYPT))
+    buff[0] = '-';
+  else
+    buff[0] = 'e';
+
+  if (!(flags & KEYFLAG_CANSIGN))
+    buff[1] = '-';
+  else
+    buff[1] = 's';
+
+  buff[2] = '\0';
+
+  return buff;
+}
 
-/* 
-   Search the certificate index for given mailbox.
-   return certificate file name.
-*/
 
 static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
 {
-  smime_id *Table = (smime_id*) menu->data;
-  smime_id this = Table[num];
+  smime_key_t **Table = (smime_key_t **) menu->data;
+  smime_key_t *this = Table[num];
   char* truststate;
-  switch(this.trust) {
+  switch(this->trust) {
     case 't':
       truststate = N_("Trusted   ");
       break;
@@ -339,298 +382,380 @@ static void smime_entry (char *s, size_t l, MUTTMENU * menu, int num)
     default:
       truststate = N_("Unknown   ");
   }
-  if (this.public)
-    snprintf(s, l, " 0x%.8X.%i %s %-35.35s %s", this.hash, this.suffix, truststate, this.email, this.nick);
-  else
-    snprintf(s, l, " 0x%.8X.%i %-35.35s %s", this.hash, this.suffix, this.email, this.nick);
+  snprintf(s, l, " 0x%s %s %s %-35.35s %s", this->hash,
+           smime_key_flags (this->flags), truststate, this->email, this->label);
+}
+
+
+static smime_key_t *smime_select_key (smime_key_t *keys, char *query)
+{
+  smime_key_t **table = NULL;
+  int table_size = 0;
+  int table_index = 0;
+  smime_key_t *key = NULL;
+  smime_key_t *selected_key = NULL;
+  char helpstr[LONG_STRING];
+  char buf[LONG_STRING];
+  char title[256];
+  MUTTMENU* menu;
+  char *s = "";
+  int done = 0;
+
+  for (table_index = 0, key = keys; key; key = key->next)
+  {
+    if (table_index == table_size)
+    {
+      table_size += 5;
+      safe_realloc (&table, sizeof (smime_key_t *) * table_size);
+    }
+
+    table[table_index++] = key;
+  }
+
+  snprintf(title, sizeof(title), _("S/MIME certificates matching \"%s\"."),
+    query);
+
+  /* Make Helpstring */
+  helpstr[0] = 0;
+  mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_SMIME, OP_EXIT);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+  mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_SMIME,
+      OP_GENERIC_SELECT_ENTRY);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+  mutt_make_help (buf, sizeof(buf), _("Help"), MENU_SMIME, OP_HELP);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+
+  /* Create the menu */
+  menu = mutt_new_menu(MENU_SMIME);
+  menu->max = table_index;
+  menu->make_entry = smime_entry;
+  menu->help = helpstr;
+  menu->data = table;
+  menu->title = title;
+  /* sorting keys might be done later - TODO */
+
+  mutt_clear_error();
+
+  done = 0;
+  while (!done)
+  {
+    switch (mutt_menuLoop (menu))
+    {
+      case OP_GENERIC_SELECT_ENTRY:
+        if (table[menu->current]->trust != 't')
+        {
+          switch (table[menu->current]->trust)
+          {
+            case 'i':
+            case 'r':
+            case 'e':
+              s = N_("ID is expired/disabled/revoked.");
+              break;
+            case 'u':
+              s = N_("ID has undefined validity.");
+              break;
+            case 'v':
+              s = N_("ID is not trusted.");
+              break;
+          }
+
+          snprintf (buf, sizeof (buf), _("%s Do you really want to use the key?"),
+                    _(s));
+
+          if (mutt_yesorno (buf, M_NO) != M_YES)
+          {
+            mutt_clear_error ();
+            break;
+          }
+        }
+
+        selected_key = table[menu->current];
+        done = 1;
+        break;
+      case OP_EXIT:
+        done = 1;
+        break;
+    }
+  }
+
+  mutt_menuDestroy (&menu);
+  FREE (&table);
+  set_option (OPTNEEDREDRAW);
+
+  return selected_key;
 }
 
+static smime_key_t *smime_parse_key(char *buf)
+{
+  smime_key_t *key;
+  char *pend, *p;
+  int field = 0;
+
+  key = safe_calloc (sizeof (smime_key_t), 1);
 
+  for (p = buf; p; p = pend)
+  {
+    if ((pend = strchr (p, ' ')) || (pend = strchr (p, '\n')))
+      *pend++ = 0;
 
+    /* For backward compatibility, don't count consecutive delimiters
+     * as an empty field.
+     */
+    if (!*p)
+      continue;
 
+    field++;
 
-char* smime_ask_for_key (char *prompt, char *mailbox, short public)
+    switch (field)
+    {
+      case 1:                   /* mailbox */
+        key->email = safe_strdup (p);
+        break;
+      case 2:                   /* hash */
+        key->hash = safe_strdup (p);
+        break;
+      case 3:                   /* label */
+        key->label = safe_strdup (p);
+        break;
+      case 4:                   /* issuer */
+        key->issuer = safe_strdup (p);
+        break;
+      case 5:                   /* trust */
+        key->trust = *p;
+        break;
+      case 6:                   /* purpose */
+        while (*p)
+        {
+          switch (*p++)
+          {
+            case 'e':
+              key->flags |= KEYFLAG_CANENCRYPT;
+              break;
+
+            case 's':
+              key->flags |= KEYFLAG_CANSIGN;
+              break;
+          }
+        }
+        break;
+    }
+  }
+
+  /* Old index files could be missing issuer, trust, and purpose,
+   * but anything less than that is an error. */
+  if (field < 3)
+  {
+    smime_free_key (&key);
+    return NULL;
+  }
+
+  if (field < 4)
+    key->issuer = safe_strdup ("?");
+
+  if (field < 5)
+    key->trust = 't';
+
+  if (field < 6)
+    key->flags = (KEYFLAG_CANENCRYPT | KEYFLAG_CANSIGN);
+
+  return key;
+}
+
+static smime_key_t *smime_get_candidates(char *search, short public)
 {
-  char *fname;
-  smime_id *table = 0;
-  int table_count;
   char index_file[_POSIX_PATH_MAX];
-  FILE *index;
+  FILE *fp;
   char buf[LONG_STRING];
-  char fields[5][STRING+1]; /* +1 due to use of fscanf() below. the max field width does not include the null terminator (see http://dev.mutt.org/trac/ticket/3636) */
-  int numFields, hash_suffix, done, cur; /* The current entry */
-  MUTTMENU* menu;
-  unsigned int hash;
-  char helpstr[HUGE_STRING*3];
-  char qry[256];
-  char title[256];
+  smime_key_t *key, *results, **results_end;
+
+  results = NULL;
+  results_end = &results;
 
-  if (!prompt) prompt = _("Enter keyID: ");
   snprintf(index_file, sizeof (index_file), "%s/.index",
     public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys));
-  
-  FOREVER
+
+  if ((fp = safe_fopen (index_file, "r")) == NULL)
   {
-    *qry = 0;
-    if (mutt_get_field(prompt,
-      qry, sizeof(qry), 0))
-      return NULL;
-    snprintf(title, sizeof(title), _("S/MIME certificates matching \"%s\"."),
-      qry);
+    mutt_perror (index_file);
+    return NULL;
+  }
 
-    
-    index = fopen(index_file, "r");
-    if (index == NULL) 
+  while (fgets (buf, sizeof (buf), fp))
+  {
+    if ((! *search) || mutt_stristr (buf, search))
     {
-      mutt_perror (index_file);      
-      return NULL;
-    }
-    /* Read Entries */
-    cur = 0;
-    table_count = 0;
-    while (!feof(index)) {
-        numFields = fscanf (index, MUTT_FORMAT(STRING) " %x.%i " MUTT_FORMAT(STRING), fields[0], &hash,
-          &hash_suffix, fields[2]);
-        if (public)
-          fscanf (index, MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) "\n", fields[3], fields[4]);
-  
-      /* 0=email 1=name 2=nick 3=intermediate 4=trust */
-      if (numFields < 2) continue;
-  
-      /* Check if query matches this certificate */
-      if (!mutt_stristr(fields[0], qry) &&
-          !mutt_stristr(fields[2], qry))
-        continue;
-  
-      ++table_count;
-      safe_realloc(&table, sizeof(smime_id) * table_count);
-      table[cur].hash = hash;
-      table[cur].suffix = hash_suffix;
-      strncpy(table[cur].email, fields[0], sizeof(table[cur].email));
-      strncpy(table[cur].nick, fields[2], sizeof(table[cur].nick));
-      table[cur].trust = *fields[4];
-      table[cur].public = public;
-  
-      cur++;
-    }
-    safe_fclose (&index);
-  
-    /* Make Helpstring */
-    helpstr[0] = 0;
-    mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_SMIME, OP_EXIT);
-    strcat (helpstr, buf);     /* __STRCAT_CHECKED__ */
-    mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_SMIME,
-        OP_GENERIC_SELECT_ENTRY);
-    strcat (helpstr, buf);     /* __STRCAT_CHECKED__ */
-    mutt_make_help (buf, sizeof(buf), _("Help"), MENU_SMIME, OP_HELP);
-    strcat (helpstr, buf);     /* __STRCAT_CHECKED__ */
-  
-    /* Create the menu */
-    menu = mutt_new_menu(MENU_SMIME);
-    menu->max = cur;
-    menu->make_entry = smime_entry;
-    menu->help = helpstr;
-    menu->data = table;
-    menu->title = title;
-    /* sorting keys might be done later - TODO */
-  
-    mutt_clear_error();
-  
-    done = 0;
-    hash = 0;
-    while (!done) {
-      switch (mutt_menuLoop (menu)) {
-        case OP_GENERIC_SELECT_ENTRY:
-          cur = menu->current;
-         hash = 1;
-          done = 1;
-          break;
-        case OP_EXIT:
-          hash = 0;
-          done = 1;
-          break;
+      key = smime_parse_key (buf);
+      if (key)
+      {
+        *results_end = key;
+        results_end = &key->next;
       }
     }
-    if (table_count && hash)
-      safe_asprintf(&fname, "%.8x.%i", table[cur].hash, table[cur].suffix);
-    else fname = NULL;
-  
-    mutt_menuDestroy (&menu);
-    FREE (&table);
-    set_option (OPTNEEDREDRAW);
-  
-    if (fname) return fname;
   }
-}
 
+  safe_fclose (&fp);
 
+  return results;
+}
 
-char *smime_get_field_from_db (char *mailbox, char *query, short public, short may_ask)
+/* Returns the first matching key record, without prompting or checking of
+ * abilities or trust.
+ */
+static smime_key_t *smime_get_key_by_hash(char *hash, short public)
 {
-  int addr_len, query_len, found = 0, ask = 0, choice = 0;
-  char cert_path[_POSIX_PATH_MAX];
-  char buf[LONG_STRING], prompt[STRING];
-  char fields[5][STRING+1]; /* +1 due to use of fscanf() below. the max field width does not include the null terminator (see http://dev.mutt.org/trac/ticket/3636) */
-  char key[STRING];  
-  int numFields;
-  struct stat info;
-  char key_trust_level = 0;
-  FILE *fp;
+  smime_key_t *results, *result;
+  smime_key_t *match = NULL;
 
-  if(!mailbox && !query) return(NULL);
-
-  addr_len = mailbox ? mutt_strlen (mailbox) : 0;
-  query_len = query ? mutt_strlen (query) : 0;
-  
-  *key = '\0';
+  results = smime_get_candidates(hash, public);
+  for (result = results; result; result = result->next)
+  {
+    if (mutt_strcasecmp (hash, result->hash) == 0)
+    {
+      match = smime_copy_key (result);
+      break;
+    }
+  }
 
-  /* index-file format:
-     mailbox certfile label issuer_certfile trust_flags\n
+  smime_free_key (&results);
 
-     certfile is a hash value generated by openssl.
-     Note that this was done according to the OpenSSL
-     specs on their CA-directory.
+  return match;
+}
 
-  */
-  snprintf (cert_path, sizeof (cert_path), "%s/.index",
-           (public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys)));
+static smime_key_t *smime_get_key_by_addr(char *mailbox, short abilities, short public, short may_ask)
+{
+  smime_key_t *results, *result;
+  smime_key_t *matches = NULL;
+  smime_key_t **matches_end = &matches;
+  smime_key_t *match;
+  smime_key_t *trusted_match = NULL;
+  smime_key_t *valid_match = NULL;
+  smime_key_t *return_key = NULL;
+  int multi_trusted_matches = 0;
+
+  if (! mailbox)
+    return NULL;
 
-  if (!stat (cert_path, &info))
+  results = smime_get_candidates(mailbox, public);
+  for (result = results; result; result = result->next)
   {
-    if ((fp = safe_fopen (cert_path, "r")) == NULL)
+    if (abilities && !(result->flags & abilities))
     {
-      mutt_perror (cert_path);
-      return (NULL);
+      continue;
     }
 
-    while (fgets (buf, sizeof (buf) - 1, fp) != NULL)
-      if (mailbox && !(mutt_strncasecmp (mailbox, buf, addr_len)))
+    if (mutt_strcasecmp (mailbox, result->email) == 0)
+    {
+      match = smime_copy_key (result);
+      *matches_end = match;
+      matches_end = &match->next;
+
+      if (match->trust == 't')
       {
-       numFields = sscanf (buf, 
-                           MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " " 
-                           MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " " 
-                           MUTT_FORMAT(STRING) "\n", 
-                           fields[0], fields[1],
-                          fields[2], fields[3], 
-                           fields[4]);
-       if (numFields < 2)
-           continue;
-       if (mailbox && public && 
-           (*fields[4] == 'i' || *fields[4] == 'e' || *fields[4] == 'r'))
-           continue;
-
-       if (found)
-       {
-         if (public && *fields[4] == 'u' )
-           snprintf (prompt, sizeof (prompt),
-                     _("ID %s is unverified. Do you want to use it for %s ?"),
-                     fields[1], mailbox);
-         else if (public && *fields[4] == 'v' )
-           snprintf (prompt, sizeof (prompt),
-                     _("Use (untrusted!) ID %s for %s ?"),
-                     fields[1], mailbox);
-         else
-           snprintf (prompt, sizeof (prompt), _("Use ID %s for %s ?"),
-                     fields[1], mailbox);
-         if (may_ask == 0)
-           choice = M_YES;
-         if (may_ask && (choice = mutt_yesorno (prompt, M_NO)) == -1)
-         {
-           found = 0;
-           ask = 0;
-           *key = '\0';
-           break;
-         }
-         else if (choice == M_NO) 
-         {
-           ask = 1;
-           continue;
-         }
-         else if (choice == M_YES)
-         {
-           strfcpy (key, fields[1], sizeof (key));
-           ask = 0;
-           break;
-         }
-       }
-       else
-       {
-         if (public) 
-           key_trust_level = *fields[4];
-         strfcpy (key, fields[1], sizeof (key));
-       }
-       found = 1;
+        if (trusted_match &&
+            (mutt_strcasecmp (match->hash, trusted_match->hash) != 0))
+        {
+          multi_trusted_matches = 1;
+        }
+        trusted_match = match;
       }
-      else if(query)
+      else if ((match->trust == 'u') || (match->trust == 'v'))
       {
-       numFields = sscanf (buf, 
-                           MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " " 
-                           MUTT_FORMAT(STRING) " " MUTT_FORMAT(STRING) " " 
-                           MUTT_FORMAT(STRING) "\n", 
-                           fields[0], fields[1],
-                           fields[2], fields[3], 
-                           fields[4]);
-
-       /* query = label: return certificate. */
-       if (numFields >= 3 && 
-           !(mutt_strncasecmp (query, fields[2], query_len)))
-       {
-         ask = 0;
-         strfcpy (key, fields[1], sizeof (key));
-       }
-       /* query = certificate: return intermediate certificate. */
-       else if (numFields >= 4 && 
-                !(mutt_strncasecmp (query, fields[1], query_len)))
-       {
-         ask = 0;
-         strfcpy (key, fields[3], sizeof (key));
-       }
+        valid_match = match;
       }
+    }
+  }
 
-    safe_fclose (&fp);
+  smime_free_key (&results);
 
-    if (ask)
+  if (matches)
+  {
+    if (! may_ask)
     {
-      if (public && *fields[4] == 'u' )
-       snprintf (prompt, sizeof (prompt),
-                 _("ID %s is unverified. Do you want to use it for %s ?"),
-                 fields[1], mailbox);
-      else if (public && *fields[4] == 'v' )
-       snprintf (prompt, sizeof (prompt),
-                 _("Use (untrusted!) ID %s for %s ?"),
-                 fields[1], mailbox);
+      if (trusted_match)
+        return_key = smime_copy_key (trusted_match);
+      else if (valid_match)
+        return_key = smime_copy_key (valid_match);
       else
-       snprintf (prompt, sizeof(prompt), _("Use ID %s for %s ?"), key,
-                 mailbox);
-      choice = mutt_yesorno (prompt, M_NO);
-      if (choice == -1 || choice == M_NO)
-       *key = '\0';
+        return_key = NULL;
     }
-    else if (key_trust_level && may_ask)
+    else if (trusted_match && !multi_trusted_matches)
     {
-      if (key_trust_level == 'u' )
-      {
-       snprintf (prompt, sizeof (prompt),
-                 _("ID %s is unverified. Do you want to use it for %s ?"),
-                 key, mailbox);
-       choice = mutt_yesorno (prompt, M_NO);
-       if (choice != M_YES)
-         *key = '\0';
-      }
-      else if (key_trust_level == 'v' )
-      {
-       mutt_error (_("Warning: You have not yet decided to trust ID %s. (any key to continue)"), key);
-       mutt_sleep (5);
-      }
+      return_key = smime_copy_key (trusted_match);
+    }
+    else
+    {
+      return_key = smime_copy_key (smime_select_key (matches, mailbox));
     }
 
+    smime_free_key (&matches);
+  }
+
+  return return_key;
+}
+
+static smime_key_t *smime_get_key_by_str(char *str, short abilities, short public)
+{
+  smime_key_t *results, *result;
+  smime_key_t *matches = NULL;
+  smime_key_t **matches_end = &matches;
+  smime_key_t *match;
+  smime_key_t *return_key = NULL;
+
+  if (! str)
+    return NULL;
+
+  results = smime_get_candidates(str, public);
+  for (result = results; result; result = result->next)
+  {
+    if (abilities && !(result->flags & abilities))
+    {
+      continue;
+    }
+
+    if ((mutt_strcasecmp (str, result->hash) == 0) ||
+        mutt_stristr(result->email, str) ||
+        mutt_stristr(result->label, str))
+    {
+      match = smime_copy_key (result);
+      *matches_end = match;
+      matches_end = &match->next;
+    }
+  }
+
+  smime_free_key (&results);
+
+  if (matches)
+  {
+    return_key = smime_copy_key (smime_select_key (matches, str));
+    smime_free_key (&matches);
   }
 
-  /* Note: safe_strdup ("") returns NULL. */
-  return safe_strdup (key);
+  return return_key;
 }
 
 
+smime_key_t *smime_ask_for_key(char *prompt, short abilities, short public)
+{
+  smime_key_t *key;
+  char resp[SHORT_STRING];
+
+  if (!prompt) prompt = _("Enter keyID: ");
+
+  mutt_clear_error ();
+
+  FOREVER
+  {
+    resp[0] = 0;
+    if (mutt_get_field (prompt, resp, sizeof (resp), M_CLEAR) != 0)
+      return NULL;
+
+    if ((key = smime_get_key_by_str (resp, abilities, public)))
+      return key;
+
+    BEEP ();
+  }
+}
+
 
 
 /* 
@@ -640,25 +765,28 @@ char *smime_get_field_from_db (char *mailbox, char *query, short public, short m
 
 void _smime_getkeys (char *mailbox)
 {
+  smime_key_t *key = NULL;
   char *k = NULL;
   char buf[STRING];
 
-  k = smime_get_field_from_db (mailbox, NULL, 0, 1);
+  key = smime_get_key_by_addr (mailbox, KEYFLAG_CANENCRYPT, 0, 1);
 
-  if (!k)
+  if (!key)
   {
     snprintf(buf, sizeof(buf), _("Enter keyID for %s: "),
             mailbox);
-    k = smime_ask_for_key(buf, mailbox, 0);
+    key = smime_ask_for_key (buf, KEYFLAG_CANENCRYPT, 0);
   }
 
-  if (k)
+  if (key)
   {
+    k = key->hash;
+
     /* the key used last time. */
     if (*SmimeKeyToUse && 
         !mutt_strcasecmp (k, SmimeKeyToUse + mutt_strlen (SmimeKeys)+1))
     {
-      FREE (&k);
+      smime_free_key (&key);
       return;
     }
     else smime_void_passphrase ();
@@ -672,7 +800,7 @@ void _smime_getkeys (char *mailbox)
     if (mutt_strcasecmp (k, SmimeDefaultKey))
       smime_void_passphrase ();
 
-    FREE (&k);
+    smime_free_key (&key);
     return;
   }
 
@@ -735,6 +863,7 @@ void smime_getkeys (ENVELOPE *env)
 
 char *smime_findKeys (ADDRESS *adrlist, int oppenc_mode)
 {
+  smime_key_t *key = NULL;
   char *keyID, *keylist = NULL;
   size_t keylist_size = 0;
   size_t keylist_used = 0;
@@ -746,15 +875,15 @@ char *smime_findKeys (ADDRESS *adrlist, int oppenc_mode)
 
     q = p;
 
-    keyID = smime_get_field_from_db (q->mailbox, NULL, 1, !oppenc_mode);
-    if ((keyID == NULL) && (! oppenc_mode))
+    key = smime_get_key_by_addr (q->mailbox, KEYFLAG_CANENCRYPT, 1, !oppenc_mode);
+    if ((key == NULL) && (! oppenc_mode))
     {
       snprintf(buf, sizeof(buf),
               _("Enter keyID for %s: "),
               q->mailbox);
-      keyID = smime_ask_for_key(buf, q->mailbox, 1);
+      key = smime_ask_for_key (buf, KEYFLAG_CANENCRYPT, 1);
     }
-    if(!keyID)
+    if (!key)
     {
       if (! oppenc_mode)
         mutt_message (_("No (valid) certificate found for %s."), q->mailbox);
@@ -762,11 +891,13 @@ char *smime_findKeys (ADDRESS *adrlist, int oppenc_mode)
       return NULL;
     }
     
+    keyID = key->hash;
     keylist_size += mutt_strlen (keyID) + 2;
     safe_realloc (&keylist, keylist_size);
     sprintf (keylist + keylist_used, "%s\n", keyID);   /* __SPRINTF_CHECKED__ */
     keylist_used = mutt_strlen (keylist);
 
+    smime_free_key (&key);
   }
   return (keylist);
 }
@@ -1342,29 +1473,21 @@ BODY *smime_sign_message (BODY *a )
   int err = 0;
   int empty = 0;
   pid_t thepid;
-  char *intermediates = smime_get_field_from_db(NULL, SmimeDefaultKey, 1, 1);
+  smime_key_t *default_key;
+  char *intermediates;
 
   if (!SmimeDefaultKey)
   {
     mutt_error _("Can't sign: No key specified. Use Sign As.");
-    FREE (&intermediates);
     return NULL;
   }
 
-  if (!intermediates)
-  {
-    mutt_message(_("Warning: Intermediate certificate not found."));
-    intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
-  }
-
   convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
 
   mutt_mktemp (filetosign, sizeof (filetosign));
   if ((sfp = safe_fopen (filetosign, "w+")) == NULL)
   {
     mutt_perror (filetosign);
-    if (intermediates != SmimeDefaultKey)
-      FREE (&intermediates);
     return NULL;
   }
 
@@ -1374,8 +1497,6 @@ BODY *smime_sign_message (BODY *a )
     mutt_perror (signedfile);
     safe_fclose (&sfp);
     mutt_unlink (filetosign);
-    if (intermediates != SmimeDefaultKey)
-      FREE (&intermediates);
     return NULL;
   }
   
@@ -1392,8 +1513,17 @@ BODY *smime_sign_message (BODY *a )
   snprintf (SmimeCertToUse, sizeof (SmimeCertToUse), "%s/%s",
           NONULL(SmimeCertificates), SmimeDefaultKey);
   
+  default_key = smime_get_key_by_hash (SmimeDefaultKey, 1);
+  if ((! default_key) ||
+      (! mutt_strcmp ("?", default_key->issuer)))
+    intermediates = SmimeDefaultKey; /* so openssl won't complain in any case */
+  else
+    intermediates = default_key->issuer;
+
   snprintf (SmimeIntermediateToUse, sizeof (SmimeIntermediateToUse), "%s/%s",
           NONULL(SmimeCertificates), intermediates);
+
+  smime_free_key (&default_key);
   
 
 
@@ -1404,8 +1534,6 @@ BODY *smime_sign_message (BODY *a )
     safe_fclose (&smimeout);
     mutt_unlink (signedfile);
     mutt_unlink (filetosign);
-    if (intermediates != SmimeDefaultKey)
-      FREE (&intermediates);
     return NULL;
   }
   fputs (SmimePass, smimein);
@@ -1907,7 +2035,7 @@ int smime_application_smime_handler (BODY *m, STATE *s)
 
 int smime_send_menu (HEADER *msg, int *redraw)
 {
-  char *p;
+  smime_key_t *key;
   char *prompt, *letters, *choices;
   int choice;
 
@@ -2031,8 +2159,11 @@ int smime_send_menu (HEADER *msg, int *redraw)
       {
         *redraw = REDRAW_FULL;
 
-        if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0)))
-          mutt_str_replace (&SmimeDefaultKey, p);
+        if ((key = smime_ask_for_key (_("Sign as: "), KEYFLAG_CANSIGN, 0)))
+        {
+          mutt_str_replace (&SmimeDefaultKey, key->hash);
+          smime_free_key (&key);
+        }
         else
           break;
       }
@@ -2043,9 +2174,10 @@ int smime_send_menu (HEADER *msg, int *redraw)
 
     case 'a': /* sign (a)s */
 
-      if ((p = smime_ask_for_key (_("Sign as: "), NULL, 0))) 
+      if ((key = smime_ask_for_key (_("Sign as: "), KEYFLAG_CANSIGN, 0))) 
       {
-        mutt_str_replace (&SmimeDefaultKey, p);
+        mutt_str_replace (&SmimeDefaultKey, key->hash);
+        smime_free_key (&key);
           
         msg->security |= SIGN;
 
diff --git a/smime.h b/smime.h
index ce8f0d0575532eed6bef9b80eac7d347b8b507a0..b6c3242c98317314479aa2b9273ded82171067e5 100644 (file)
--- a/smime.h
+++ b/smime.h
 
 #include "mutt_crypt.h"
 
+typedef struct smime_key {
+  char *email;
+  char *hash;
+  char *label;
+  char *issuer;
+  char trust; /* i=Invalid r=revoked e=expired u=unverified v=verified t=trusted */
+  int flags;
+  struct smime_key *next;
+} smime_key_t;
 
 
-
+void smime_free_key (smime_key_t **);
 
 void smime_void_passphrase (void);
 int smime_valid_passphrase (void);
@@ -48,7 +57,7 @@ char* smime_get_field_from_db (char *, char *, short, short);
 
 void  smime_getkeys (ENVELOPE *);
 
-char* smime_ask_for_key (char *, char *, short);
+smime_key_t *smime_ask_for_key(char *, short, short);
 
 char *smime_findKeys (ADDRESS *adrlist, int oppenc_mode);