Complete the change for VMS.
[openssl.git] / crypto / x509 / by_dir.c
index 6676a2e4042ae17dcb85295f59f85eb198f88cf5..098e86819935d8911b84ed956aeb099112649046 100644 (file)
@@ -1,5 +1,5 @@
 /* crypto/x509/by_dir.c */
-/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
  * This package is an SSL implementation written
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 
 #include "cryptlib.h"
-#include "lhash.h"
-#include "x509.h"
-#include "pem.h"
+
+#ifndef NO_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifndef OPENSSL_NO_POSIX_IO
+# include <sys/stat.h>
+#endif
+
+#include <openssl/lhash.h>
+#include <openssl/x509.h>
+
+DECLARE_STACK_OF(BY_DIR_HASH)
+DECLARE_STACK_OF(BY_DIR_ENTRY)
+
+typedef struct lookup_dir_hashes_st
+       {
+       unsigned long hash;
+       int suffix;
+       } BY_DIR_HASH;
+
+typedef struct lookup_dir_entry_st
+       {
+       char *dir;
+       int dir_type;
+       STACK_OF(BY_DIR_HASH) *hashes;
+       } BY_DIR_ENTRY;
 
 typedef struct lookup_dir_st
        {
        BUF_MEM *buffer;
-       int num_dirs;
-       char **dirs;
-       int *dirs_type;
-       int num_dirs_alloced;
+       STACK_OF(BY_DIR_ENTRY) *dirs;
        } BY_DIR;
 
-#ifndef NOPROTO
-static int dir_ctrl(X509_LOOKUP *ctx,int cmd,char *argp,long argl,char **ret);
+
+static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
+       char **ret);
 static int new_dir(X509_LOOKUP *lu);
 static void free_dir(X509_LOOKUP *lu);
-static int add_cert_dir(BY_DIR *ctx,char *dir,int type);
+static int add_cert_dir(BY_DIR *ctx,const char *dir,int type);
 static int get_cert_by_subject(X509_LOOKUP *xl,int type,X509_NAME *name,
        X509_OBJECT *ret);
-#else
-static int dir_ctrl();
-static int new_dir();
-static void free_dir();
-static int add_cert_dir();
-static int get_cert_by_subject();
-#endif
-
 X509_LOOKUP_METHOD x509_dir_lookup=
        {
        "Load certs from files in a directory",
@@ -105,21 +116,17 @@ X509_LOOKUP_METHOD x509_dir_lookup=
        NULL,                   /* get_by_alias */
        };
 
-X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir()
+X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void)
        {
        return(&x509_dir_lookup);
        }
 
-static int dir_ctrl(ctx,cmd,argp,argl,retp)
-X509_LOOKUP *ctx;
-int cmd;
-long argl;
-char *argp;
-char **retp;
+static int dir_ctrl(X509_LOOKUP *ctx, int cmd, const char *argp, long argl,
+            char **retp)
        {
        int ret=0;
        BY_DIR *ld;
-       char *dir;
+       char *dir = NULL;
 
        ld=(BY_DIR *)ctx->method_data;
 
@@ -128,17 +135,16 @@ char **retp;
        case X509_L_ADD_DIR:
                if (argl == X509_FILETYPE_DEFAULT)
                        {
-                       ret=add_cert_dir(ld,X509_get_default_cert_dir(),
-                               X509_FILETYPE_PEM);
+                       dir=(char *)getenv(X509_get_default_cert_dir_env());
+                       if (dir)
+                               ret=add_cert_dir(ld,dir,X509_FILETYPE_PEM);
+                       else
+                               ret=add_cert_dir(ld,X509_get_default_cert_dir(),
+                                       X509_FILETYPE_PEM);
                        if (!ret)
                                {
                                X509err(X509_F_DIR_CTRL,X509_R_LOADING_CERT_DIR);
                                }
-                       else
-                               {
-                               dir=(char *)Getenv(X509_get_default_cert_dir_env());
-                               ret=add_cert_dir(ld,dir,X509_FILETYPE_PEM);
-                               }
                        }
                else
                        ret=add_cert_dir(ld,argp,(int)argl);
@@ -147,52 +153,68 @@ char **retp;
        return(ret);
        }
 
-static int new_dir(lu)
-X509_LOOKUP *lu;
+static int new_dir(X509_LOOKUP *lu)
        {
        BY_DIR *a;
 
-       if ((a=(BY_DIR *)Malloc(sizeof(BY_DIR))) == NULL)
+       if ((a=(BY_DIR *)OPENSSL_malloc(sizeof(BY_DIR))) == NULL)
                return(0);
        if ((a->buffer=BUF_MEM_new()) == NULL)
                {
-               Free(a);
+               OPENSSL_free(a);
                return(0);
                }
-       a->num_dirs=0;
        a->dirs=NULL;
-       a->dirs_type=NULL;
-       a->num_dirs_alloced=0;
        lu->method_data=(char *)a;
        return(1);
        }
 
-static void free_dir(lu)
-X509_LOOKUP *lu;
+static void by_dir_hash_free(BY_DIR_HASH *hash)
+       {
+       OPENSSL_free(hash);
+       }
+
+static int by_dir_hash_cmp(const BY_DIR_HASH * const *a,
+                       const BY_DIR_HASH * const *b)
+       {
+       if ((*a)->hash > (*b)->hash)
+               return 1;
+       if ((*a)->hash < (*b)->hash)
+               return -1;
+       return 0;
+       }
+
+static void by_dir_entry_free(BY_DIR_ENTRY *ent)
+       {
+       if (ent->dir)
+               OPENSSL_free(ent->dir);
+       if (ent->hashes)
+               sk_BY_DIR_HASH_pop_free(ent->hashes, by_dir_hash_free);
+       OPENSSL_free(ent);
+       }
+
+static void free_dir(X509_LOOKUP *lu)
        {
        BY_DIR *a;
-       int i;
 
        a=(BY_DIR *)lu->method_data;
-       for (i=0; i<a->num_dirs; i++)
-               if (a->dirs[i] != NULL) Free(a->dirs[i]);
-       if (a->dirs != NULL) Free(a->dirs);
-       if (a->dirs_type != NULL) Free(a->dirs_type);
-       if (a->buffer != NULL) BUF_MEM_free(a->buffer);
-       Free(a);
+       if (a->dirs != NULL)
+               sk_BY_DIR_ENTRY_pop_free(a->dirs, by_dir_entry_free);
+       if (a->buffer != NULL)
+               BUF_MEM_free(a->buffer);
+       OPENSSL_free(a);
        }
 
-static int add_cert_dir(ctx,dir, type)
-BY_DIR *ctx;
-char *dir;
-int type;
+static int add_cert_dir(BY_DIR *ctx, const char *dir, int type)
        {
        int j,len;
-       int *ip;
-       char *s,*ss,*p;
-       char **pp;
+       const char *s,*ss,*p;
 
-       if (dir == NULL) return(0);
+       if (dir == NULL || !*dir)
+           {
+           X509err(X509_F_ADD_CERT_DIR,X509_R_INVALID_DIRECTORY);
+           return 0;
+           }
 
        s=dir;
        p=s;
@@ -200,71 +222,96 @@ int type;
                {
                if ((*p == LIST_SEPARATOR_CHAR) || (*p == '\0'))
                        {
+                       BY_DIR_ENTRY *ent;
                        ss=s;
                        s=p+1;
                        len=(int)(p-ss);
                        if (len == 0) continue;
-                       for (j=0; j<ctx->num_dirs; j++)
-                               if (strncmp(ctx->dirs[j],ss,(unsigned int)len) == 0)
+                       for (j=0; j < sk_BY_DIR_ENTRY_num(ctx->dirs); j++)
+                               {
+                               ent = sk_BY_DIR_ENTRY_value(ctx->dirs, j);
+                               if (strncmp(ent->dir,ss,(unsigned int)len) == 0)
                                        continue;
-                       if (ctx->num_dirs_alloced < (ctx->num_dirs+1))
+                               }
+                       
+                       if (ctx->dirs == NULL)
                                {
-                               ctx->num_dirs_alloced+=10;
-                               pp=(char **)Malloc(ctx->num_dirs_alloced*
-                                       sizeof(char *));
-                               ip=(int *)Malloc(ctx->num_dirs_alloced*
-                                       sizeof(int));
-                               if ((pp == NULL) || (ip == NULL))
+                               ctx->dirs = sk_BY_DIR_ENTRY_new_null();
+                               if (!ctx->dirs)
                                        {
                                        X509err(X509_F_ADD_CERT_DIR,ERR_R_MALLOC_FAILURE);
-                                       return(0);
+                                       return 0;
                                        }
-                               memcpy(pp,ctx->dirs,(ctx->num_dirs_alloced-10)*
-                                       sizeof(char *));
-                               memcpy(ip,ctx->dirs_type,(ctx->num_dirs_alloced-10)*
-                                       sizeof(int));
-                               if (ctx->dirs != NULL)
-                                       Free((char *)ctx->dirs);
-                               if (ctx->dirs_type != NULL)
-                                       Free((char *)ctx->dirs_type);
-                               ctx->dirs=pp;
-                               ctx->dirs_type=ip;
                                }
-                       ctx->dirs_type[ctx->num_dirs]=type;
-                       ctx->dirs[ctx->num_dirs]=(char *)Malloc((unsigned int)len+1);
-                       if (ctx->dirs[ctx->num_dirs] == NULL) return(0);
-                       strncpy(ctx->dirs[ctx->num_dirs],ss,(unsigned int)len);
-                       ctx->dirs[ctx->num_dirs][len]='\0';
-                       ctx->num_dirs++;
+                       ent = OPENSSL_malloc(sizeof(BY_DIR_ENTRY));
+                       if (!ent)
+                               return 0;
+                       ent->dir_type = type;
+                       ent->hashes = sk_BY_DIR_HASH_new(by_dir_hash_cmp);
+                       ent->dir = OPENSSL_malloc((unsigned int)len+1);
+                       if (!ent->dir || !ent->hashes)
+                               {
+                               by_dir_entry_free(ent);
+                               return 0;
+                               }
+                       strncpy(ent->dir,ss,(unsigned int)len);
+                       ent->dir[len] = '\0';
+                       if (!sk_BY_DIR_ENTRY_push(ctx->dirs, ent))
+                               {
+                               by_dir_entry_free(ent);
+                               return 0;
+                               }
                        }
-               if (*p == '\0') break;
+               if (*p == '\0')
+                       break;
                p++;
                }
-       return(1);
+       return 1;
        }
 
-static int get_cert_by_subject(xl,type,name,ret)
-X509_LOOKUP *xl;
-int type;
-X509_NAME *name;
-X509_OBJECT *ret;
+static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
+            X509_OBJECT *ret)
        {
        BY_DIR *ctx;
-       X509 st_x509;
-       X509_CINF st_x509_cinf;
+       union   {
+               struct  {
+                       X509 st_x509;
+                       X509_CINF st_x509_cinf;
+                       } x509;
+               struct  {
+                       X509_CRL st_crl;
+                       X509_CRL_INFO st_crl_info;
+                       } crl;
+               } data;
        int ok=0;
        int i,j,k;
        unsigned long h;
        BUF_MEM *b=NULL;
-       struct stat st;
        X509_OBJECT stmp,*tmp;
+       const char *postfix="";
 
        if (name == NULL) return(0);
 
-       st_x509.cert_info= &st_x509_cinf;
-       st_x509_cinf.subject=name;
-       stmp.data.x509= &st_x509;
        stmp.type=type;
+       if (type == X509_LU_X509)
+               {
+               data.x509.st_x509.cert_info= &data.x509.st_x509_cinf;
+               data.x509.st_x509_cinf.subject=name;
+               stmp.data.x509= &data.x509.st_x509;
+               postfix="";
+               }
+       else if (type == X509_LU_CRL)
+               {
+               data.crl.st_crl.crl= &data.crl.st_crl_info;
+               data.crl.st_crl_info.issuer=name;
+               stmp.data.crl= &data.crl.st_crl;
+               postfix="r";
+               }
+       else
+               {
+               X509err(X509_F_GET_CERT_BY_SUBJECT,X509_R_WRONG_LOOKUP_TYPE);
+               goto finish;
+               }
 
        if ((b=BUF_MEM_new()) == NULL)
                {
@@ -275,39 +322,148 @@ X509_OBJECT *ret;
        ctx=(BY_DIR *)xl->method_data;
 
        h=X509_NAME_hash(name);
-       for (i=0; i<ctx->num_dirs; i++)
+       for (i=0; i < sk_BY_DIR_ENTRY_num(ctx->dirs); i++)
                {
-               j=strlen(ctx->dirs[i])+1+8+6+1;
+               BY_DIR_ENTRY *ent;
+               int idx;
+               BY_DIR_HASH htmp, *hent;
+               ent = sk_BY_DIR_ENTRY_value(ctx->dirs, i);
+               j=strlen(ent->dir)+1+8+6+1+1;
                if (!BUF_MEM_grow(b,j))
                        {
                        X509err(X509_F_GET_CERT_BY_SUBJECT,ERR_R_MALLOC_FAILURE);
                        goto finish;
                        }
-               k=0;
+               if (type == X509_LU_CRL && ent->hashes)
+                       {
+                       htmp.hash = h;
+                       CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
+                       idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
+                       if (idx >= 0)
+                               {
+                               hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
+                               k = hent->suffix;
+                               }
+                       else
+                               {
+                               hent = NULL;
+                               k=0;
+                               }
+                       CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
+                       }
+               else
+                       k = 0;
                for (;;)
                        {
-                       sprintf(b->data,"%s/%08lx.%d",ctx->dirs[i],h,k);
-                       k++;
+                       char c = '/';
+#ifdef OPENSSL_SYS_VMS
+                       c = ent->dir[strlen(ent->dir)-1];
+                       if (c != ':' && c != '>' && c != ']')
+                               {
+                               /* If no separator is present, we assume the
+                                  directory specifier is a logical name, and
+                                  add a colon.  We really should use better
+                                  VMS routines for merging things like this,
+                                  but this will do for now...
+                                  -- Richard Levitte */
+                               c = ':';
+                               }
+                       else
+                               {
+                               c = '\0';
+                               }
+#endif
+                       if (c == '\0')
+                               {
+                               /* This is special.  When c == '\0', no
+                                  directory separator should be added. */
+                               BIO_snprintf(b->data,b->max,
+                                       "%s%08lx.%s%d",ent->dir,h,
+                                       postfix,k);
+                               }
+                       else
+                               {
+                               BIO_snprintf(b->data,b->max,
+                                       "%s%c%08lx.%s%d",ent->dir,c,h,
+                                       postfix,k);
+                               }
+#ifndef OPENSSL_NO_POSIX_IO
+                       {
+                       struct stat st;
                        if (stat(b->data,&st) < 0)
                                break;
+                       }
+#endif
                        /* found one. */
-                       if ((X509_load_cert_file(xl,b->data,
-                               ctx->dirs_type[i])) == 0)
+                       if (type == X509_LU_X509)
+                               {
+                               if ((X509_load_cert_file(xl,b->data,
+                                       ent->dir_type)) == 0)
+                                       break;
+                               }
+                       else if (type == X509_LU_CRL)
+                               {
+                               if ((X509_load_crl_file(xl,b->data,
+                                       ent->dir_type)) == 0)
                                        break;
+                               }
+                       /* else case will caught higher up */
+                       k++;
                        }
 
                /* we have added it to the cache so now pull
                 * it out again */
                CRYPTO_r_lock(CRYPTO_LOCK_X509_STORE);
-               tmp=(X509_OBJECT *)lh_retrieve(xl->store_ctx->certs,
-                       (char *)&stmp);
+               j = sk_X509_OBJECT_find(xl->store_ctx->objs,&stmp);
+               if(j != -1) tmp=sk_X509_OBJECT_value(xl->store_ctx->objs,j);
+               else tmp = NULL;
                CRYPTO_r_unlock(CRYPTO_LOCK_X509_STORE);
 
+
+               /* If a CRL, update the last file suffix added for this */
+
+               if (type == X509_LU_CRL)
+                       {
+                       CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+                       /* Look for entry again in case another thread added
+                        * an entry first.
+                        */
+                       if (!hent)
+                               {
+                               htmp.hash = h;
+                               idx = sk_BY_DIR_HASH_find(ent->hashes, &htmp);
+                               if (idx >= 0)
+                                       hent =
+                                        sk_BY_DIR_HASH_value(ent->hashes, idx);
+                               }
+                       if (!hent)
+                               {
+                               hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
+                               hent->hash = h;
+                               hent->suffix = k;
+                               if (!sk_BY_DIR_HASH_push(ent->hashes, hent))
+                                       {
+                                       CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+                                       OPENSSL_free(hent);
+                                       ok = 0;
+                                       goto finish;
+                                       }
+                               }
+                       else if (hent->suffix < k)
+                               hent->suffix = k;
+
+                       CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+
+                       }
+
                if (tmp != NULL)
                        {
                        ok=1;
                        ret->type=tmp->type;
-                       ret->data.x509=tmp->data.x509;
+                       memcpy(&ret->data,&tmp->data,sizeof(ret->data));
+                       /* If we were going to up the reference count,
+                        * we would need to do it on a perl 'type'
+                        * basis */
        /*              CRYPTO_add(&tmp->data.x509->references,1,
                                CRYPTO_LOCK_X509);*/
                        goto finish;