summaryrefslogtreecommitdiffstats
path: root/tcpd/tlspasswordcache.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /tcpd/tlspasswordcache.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'tcpd/tlspasswordcache.c')
-rw-r--r--tcpd/tlspasswordcache.c954
1 files changed, 954 insertions, 0 deletions
diff --git a/tcpd/tlspasswordcache.c b/tcpd/tlspasswordcache.c
new file mode 100644
index 0000000..5f3ca2b
--- /dev/null
+++ b/tcpd/tlspasswordcache.c
@@ -0,0 +1,954 @@
+/*
+** Copyright 2003-2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include "config.h"
+#include "tlspasswordcache.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <md5/md5.h>
+
+#define PASSFILEFORMAT 1
+
+#if HAVE_OPENSSL097
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+static void sslerror(EVP_CIPHER_CTX *ctx, const char *pfix)
+{
+ char errmsg[256];
+ int errnum=ERR_get_error();
+
+ ERR_error_string_n(errnum, errmsg, sizeof(errmsg)-1);
+
+ fprintf(stderr, "%s: %s\n", pfix, errmsg);
+}
+
+
+#endif
+
+#if HAVE_GCRYPT
+
+#include <gcrypt.h>
+
+#define RAND_pseudo_bytes(a,b) (gcry_create_nonce((a),(b)), 0)
+
+typedef struct {
+ enum gcry_cipher_algos algo;
+ enum gcry_cipher_modes mode;
+} EVP_CIPHER;
+
+#define EVP_MAX_IV_LENGTH 256
+
+const EVP_CIPHER *EVP_des_cbc()
+{
+ static const EVP_CIPHER des_cbc={GCRY_CIPHER_DES,
+ GCRY_CIPHER_MODE_CBC};
+
+ return &des_cbc;
+}
+
+typedef struct {
+ const EVP_CIPHER *cipher;
+ gcry_error_t err;
+ gcry_cipher_hd_t handle;
+
+ int padding;
+ char *blkbuf;
+ size_t blksize;
+
+ size_t blkptr;
+
+} EVP_CIPHER_CTX;
+
+static void sslerror(EVP_CIPHER_CTX *ctx, const char *pfix)
+{
+ fprintf(stderr, "%s: %s\n", pfix, gcry_strerror(ctx->err));
+}
+
+static void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static void EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ if (ctx->handle)
+ {
+ gcry_cipher_close(ctx->handle);
+ ctx->handle=NULL;
+ }
+
+ if (ctx->blkbuf)
+ {
+ free(ctx->blkbuf);
+ ctx->blkbuf=NULL;
+ }
+}
+
+static int EVP_CIPHER_iv_length(const EVP_CIPHER *cipher)
+{
+ size_t l=0;
+
+ gcry_cipher_algo_info(cipher->algo, GCRYCTL_GET_BLKLEN, NULL, &l);
+ return l;
+}
+
+static int EVP_CIPHER_key_length(const EVP_CIPHER *cipher)
+{
+ size_t l=0;
+
+ gcry_cipher_algo_info(cipher->algo, GCRYCTL_GET_KEYLEN, NULL, &l);
+ return l;
+}
+
+static int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ void *impl, unsigned char *key, unsigned char *iv)
+{
+ EVP_CIPHER_CTX_cleanup(ctx);
+ ctx->cipher=cipher;
+ ctx->err=gcry_cipher_open(&ctx->handle,
+ cipher->algo,
+ cipher->mode, 0);
+
+ if (!ctx->err)
+ ctx->err=gcry_cipher_setkey(ctx->handle, key,
+ EVP_CIPHER_key_length(cipher));
+
+ if (!ctx->err)
+ ctx->err=gcry_cipher_setiv(ctx->handle, iv,
+ (ctx->blksize=
+ EVP_CIPHER_iv_length(cipher)));
+
+ if (!ctx->err)
+ if ((ctx->blkbuf=malloc(ctx->blksize)) == NULL)
+ ctx->err=gpg_err_code_from_errno(errno);
+
+ ctx->blkptr=0;
+ ctx->padding=1;
+
+ return ctx->err == 0;
+}
+
+static int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl, unsigned char *in, int inl)
+{
+ *outl=0;
+
+ while (inl > 0)
+ {
+ size_t cp= (size_t)inl < (ctx->blksize - ctx->blkptr)
+ ? (size_t)inl:(ctx->blksize - ctx->blkptr);
+
+ if (ctx->blkptr == 0 && inl > ctx->blksize*2)
+ {
+ cp=(inl / ctx->blksize - 1) * ctx->blksize;
+
+ if ((ctx->err=gcry_cipher_encrypt(ctx->handle,
+ out, cp,
+ in, cp))
+ != 0)
+ return 0;
+
+ out += cp;
+ *outl += cp;
+ in += cp;
+ inl -= cp;
+ continue;
+ }
+
+ memcpy(ctx->blkbuf + ctx->blkptr, in, cp);
+
+ in += cp;
+ inl -= cp;
+
+ ctx->blkptr += cp;
+
+ if (ctx->blkptr == ctx->blksize)
+ {
+ if ((ctx->err=gcry_cipher_encrypt(ctx->handle,
+ out, ctx->blksize,
+ ctx->blkbuf,
+ ctx->blksize)) != 0)
+ return 0;
+ out += ctx->blksize;
+ *outl += ctx->blksize;
+ ctx->blkptr=0;
+ }
+ }
+ return 1;
+}
+
+static int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl)
+{
+ if (ctx->padding)
+ {
+ unsigned char pad=ctx->blksize - ctx->blkptr;
+
+ *outl=0;
+
+ if (pad == 0)
+ pad=ctx->blksize;
+
+ do
+ {
+ int n_outl;
+
+ if (!EVP_EncryptUpdate(ctx, out, &n_outl, &pad, 1))
+ return 0;
+
+ out += n_outl;
+ *outl += n_outl;
+ }
+ while (ctx->blkptr);
+ }
+ else if (ctx->blksize != ctx->blkptr)
+ {
+ ctx->err=GPG_ERR_BAD_DATA;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
+ void *impl, unsigned char *key,
+ unsigned char *iv)
+{
+ return EVP_EncryptInit_ex(ctx, type, impl, key, iv);
+}
+
+static int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ int *outl, unsigned char *in, int inl)
+{
+ *outl=0;
+
+ while (inl > 0)
+ {
+ size_t cp;
+
+ if (ctx->blkptr == 0 && inl > ctx->blksize * 3)
+ {
+ cp=(inl / ctx->blksize - 2) * ctx->blksize;
+
+ if ((ctx->err=gcry_cipher_decrypt(ctx->handle,
+ out, cp,
+ in, cp))
+ != 0)
+ return 0;
+
+ out += cp;
+ *outl += cp;
+ in += cp;
+ inl -= cp;
+ continue;
+ }
+
+ if (ctx->blkptr == ctx->blksize)
+ {
+ if ((ctx->err=gcry_cipher_decrypt(ctx->handle,
+ out, ctx->blksize,
+ ctx->blkbuf,
+ ctx->blksize)) != 0)
+ return 0;
+ out += ctx->blksize;
+ *outl += ctx->blksize;
+ ctx->blkptr=0;
+ }
+
+ cp= (size_t)inl < (ctx->blksize - ctx->blkptr)
+ ? (size_t)inl:(ctx->blksize - ctx->blkptr);
+
+ memcpy(ctx->blkbuf + ctx->blkptr, in, cp);
+
+ in += cp;
+ inl -= cp;
+
+ ctx->blkptr += cp;
+
+ }
+ return 1;
+}
+
+static int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,
+ int *outl)
+{
+ unsigned char lastval;
+ int cnt;
+
+ if (ctx->blkptr != ctx->blksize)
+ {
+ ctx->err=GPG_ERR_BAD_DATA;
+ return 0;
+ }
+
+ if ((ctx->err=gcry_cipher_decrypt(ctx->handle,
+ ctx->blkbuf,
+ ctx->blksize,
+ NULL, 0)) != 0)
+ return 0;
+
+ if (ctx->padding)
+ {
+ lastval=ctx->blkbuf[ctx->blksize-1];
+
+ if (lastval > 0 && lastval <= ctx->blksize)
+ {
+ char n;
+
+ for (n=0; n<lastval; n++)
+ if (ctx->blkbuf[ctx->blksize-1-n] != lastval)
+ lastval=0;
+ }
+ else
+ lastval=0;
+
+ if (!lastval)
+ {
+ ctx->err=GPG_ERR_BAD_DATA;
+ return 0;
+ }
+ }
+ else
+ {
+ lastval=0;
+ }
+
+ cnt=ctx->blksize-lastval;
+ if (cnt)
+ memcpy(outm, ctx->blkbuf, cnt);
+ *outl=cnt;
+ return 1;
+}
+
+
+#define HAVE_OPENSSL097 1
+#endif
+
+#if HAVE_OPENSSL097
+
+#if BUFSIZ < 8192
+#undef BUFSIZ
+#define BUFSIZ 8192
+#endif
+
+int tlspassword_init()
+{
+ return 1;
+}
+
+static int save_string(EVP_CIPHER_CTX *,
+ const char *, char *,
+ int (*)(const char *, size_t, void *),
+ void *);
+
+int tlspassword_save( const char * const *urls,
+ const char * const *pwds,
+ const char *mpw,
+ int (*writefunc)(const char *, size_t, void *),
+ void *writefuncarg)
+{
+ char buf[BUFSIZ];
+ char *p;
+ int l;
+ int wl;
+
+ unsigned char iv1_buf[16];
+ unsigned char iv2_buf[16];
+ MD5_DIGEST md5_password;
+ int iv_len, key_len;
+ EVP_CIPHER_CTX ctx;
+ const EVP_CIPHER *des=EVP_des_cbc();
+
+ md5_digest(mpw, strlen(mpw), md5_password);
+
+ EVP_CIPHER_CTX_init(&ctx);
+ iv_len=EVP_CIPHER_iv_length(des);
+ key_len=EVP_CIPHER_key_length(des);
+
+ if (RAND_pseudo_bytes(iv1_buf, sizeof(iv1_buf)) < 0 ||
+ RAND_pseudo_bytes(iv2_buf, sizeof(iv2_buf)) < 0)
+ {
+ fprintf(stderr,
+ "tlspassword_save: internal error - "
+ "RAND_pseudo_bytes() failed.\n");
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ errno=EIO;
+ return -1;
+ }
+
+ if (iv_len + key_len > sizeof(iv1_buf)
+ || iv_len + key_len != sizeof(iv2_buf)
+ || key_len != sizeof(md5_password)/2)
+ {
+ fprintf(stderr,
+ "tlspassword_save: internal error - "
+ "unexpected key sizes.\n");
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ errno=EIO;
+ return -1;
+ }
+
+ p=buf+3;
+
+ if (!EVP_EncryptInit_ex(&ctx, des, NULL,
+ (unsigned char *)md5_password,
+ iv1_buf) ||
+ !EVP_EncryptUpdate(&ctx, (unsigned char *)p, &l,
+ (unsigned char *)md5_password + key_len,
+ sizeof(md5_password)-key_len) ||
+ !EVP_EncryptUpdate(&ctx, (unsigned char *)(p += l), &l,
+ iv2_buf,
+ iv_len + key_len) ||
+ !EVP_EncryptFinal_ex(&ctx, (unsigned char *)(p += l), &l))
+
+ {
+ sslerror(&ctx, "EVP_EncryptInit_ex");
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ errno=EIO;
+ return -1;
+ }
+
+ p += l;
+
+ wl= p - buf - 3;
+
+ buf[0]=PASSFILEFORMAT;
+ buf[1]= wl / 256;
+ buf[2]= wl % 256;
+
+ l=(*writefunc)(buf, 3, writefuncarg);
+
+ if (l == 0)
+ l=(*writefunc)((const char *)iv1_buf, iv_len, writefuncarg);
+
+ if (l == 0)
+ l=(*writefunc)(buf+3, wl, writefuncarg);
+
+ if (l)
+ return l;
+
+#if 0
+ {
+ int i;
+
+ printf("KEY: ");
+
+ for (i=0; i<key_len + iv_len; i++)
+ printf("%02X", (int)(unsigned char)iv2_buf[i]);
+ printf("\n");
+ }
+#endif
+
+ if (!EVP_EncryptInit_ex(&ctx, des, NULL,
+ (unsigned char *)&iv2_buf,
+ (unsigned char *)&iv2_buf + key_len))
+ {
+ sslerror(&ctx, "EVP_EncryptInit_ex");
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ errno=EIO;
+ return -1;
+ }
+
+ for (l=0; urls[l]; l++)
+ {
+ int n=save_string(&ctx, urls[l], buf, writefunc, writefuncarg);
+
+ if (n)
+ return n;
+
+ n=save_string(&ctx, pwds[l], buf, writefunc, writefuncarg);
+
+ if (n)
+ return n;
+ }
+
+ if (!EVP_EncryptFinal_ex(&ctx, (unsigned char *)buf, &l))
+ {
+ sslerror(&ctx, "EVP_EncryptInit_ex");
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ errno=EIO;
+ return -1;
+ }
+
+ if (l)
+ l=(*writefunc)(buf, l, writefuncarg);
+
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return l;
+}
+
+static int save_string(EVP_CIPHER_CTX *ctx,
+ const char *str, char *buf,
+ int (*writefunc)(const char *, size_t, void *),
+ void *writefuncarg)
+{
+ int l;
+ size_t len=strlen(str);
+ unsigned char b[2];
+
+ if (len >= 256 * 256)
+ {
+ fprintf(stderr,
+ "tlspassword_save: internal error - "
+ "key sizes too large.\n");
+ errno=EINVAL;
+ return -1;
+ }
+
+ b[0]=len / 256;
+ b[1]=len % 256;
+
+ if (!EVP_EncryptUpdate(ctx, (unsigned char *)buf, &l, b, 2))
+ {
+ sslerror(ctx, "EVP_EncryptUpdate");
+ return -1;
+ }
+
+ if (l)
+ {
+ l=(*writefunc)(buf, l, writefuncarg);
+
+ if (l)
+ return l;
+ }
+
+ while (len)
+ {
+ size_t n=len;
+
+ if (n > BUFSIZ / 4)
+ n=BUFSIZ/4;
+
+ if (!EVP_EncryptUpdate(ctx, (unsigned char *)buf, &l,
+ (unsigned char *)str, n))
+ {
+ sslerror(ctx, "EVP_EncryptUpdate");
+ return -1;
+ }
+
+ if (l)
+ {
+ l=(*writefunc)(buf, l, writefuncarg);
+
+ if (l)
+ return l;
+ }
+
+ str += n;
+ len -= n;
+ }
+
+ return 0;
+}
+
+struct tempstring_list {
+ struct tempstring_list *next;
+ char *url;
+ char *pw;
+};
+
+struct tlspassword_readinfo {
+ char buf[BUFSIZ / 2];
+ char *bufptr;
+ size_t bufleft;
+
+ int (*readfunc)(char *, size_t, void *);
+ void *readfuncarg;
+
+ struct tempstring_list *tl_list, *tl_last;
+
+ int (*readhandler)(struct tlspassword_readinfo *, char *, int);
+
+ unsigned int stringhi;
+ char *stringptr;
+ size_t stringleft;
+ size_t nstrings;
+};
+
+
+static int tlspassword_read(struct tlspassword_readinfo *p,
+ char *buf,
+ size_t nbytes)
+{
+ while (nbytes)
+ {
+ size_t c;
+
+ if (p->bufleft == 0)
+ {
+ int n= (*p->readfunc)(p->buf, sizeof(p->buf),
+ p->readfuncarg);
+
+ if (n <= 0)
+ return -1;
+ p->bufptr=p->buf;
+ p->bufleft=n;
+ }
+
+ c=nbytes;
+
+ if (c > p->bufleft)
+ c=p->bufleft;
+
+ memcpy(buf, p->bufptr, c);
+ p->bufptr += c;
+ p->bufleft -= c;
+ nbytes -= c;
+ }
+
+ return 0;
+}
+
+static void tlspassword_readcleanup(struct tlspassword_readinfo *p)
+{
+ while (p->tl_list)
+ {
+ struct tempstring_list *t=p->tl_list;
+
+ p->tl_list=t->next;
+ if (t->url)
+ free(t->url);
+ if (t->pw)
+ free(t->pw);
+ free(t);
+ }
+}
+
+static int read_stringhi(struct tlspassword_readinfo *, char *, int);
+
+int tlspassword_load( int (*callback)(char *, size_t, void *),
+ void *callback_arg,
+
+ const char *mpw,
+
+ void (*readfunc)(const char * const *,
+ const char * const *,
+ void *),
+ void *readfunc_arg)
+{
+ char buf[BUFSIZ];
+ int outl;
+ char *p;
+
+ MD5_DIGEST md5_password;
+ int iv_len, key_len;
+ EVP_CIPHER_CTX ctx;
+ const EVP_CIPHER *des=EVP_des_cbc();
+ struct tlspassword_readinfo readinfo;
+ char header[3];
+ size_t l;
+ char iv1_buf[EVP_MAX_IV_LENGTH];
+ struct tempstring_list *tl;
+ const char **urls, **pws;
+
+ readinfo.bufleft=0;
+ readinfo.readfunc=callback;
+ readinfo.readfuncarg=callback_arg;
+ readinfo.tl_list=NULL;
+ readinfo.tl_last=NULL;
+
+ md5_digest(mpw, strlen(mpw), md5_password);
+
+ EVP_CIPHER_CTX_init(&ctx);
+ iv_len=EVP_CIPHER_iv_length(des);
+ key_len=EVP_CIPHER_key_length(des);
+
+ if (tlspassword_read(&readinfo, header, 3) ||
+ tlspassword_read(&readinfo, iv1_buf, iv_len))
+ {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+ if (header[0] != PASSFILEFORMAT)
+ {
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ if ((l=(size_t)(unsigned char)header[1] * 256
+ + (unsigned char)header[2]) > sizeof(buf) / 4)
+ {
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ if (tlspassword_read(&readinfo, buf, l))
+ return -1;
+
+ p=buf + sizeof(buf)/2;
+ if (!EVP_DecryptInit_ex(&ctx, des, NULL,
+ (unsigned char *)md5_password,
+ (unsigned char *)&iv1_buf) ||
+ !EVP_DecryptUpdate(&ctx, (unsigned char *)p, &outl,
+ (unsigned char *)buf, l) ||
+ !EVP_DecryptFinal_ex(&ctx, (unsigned char *)(p += outl), &outl))
+ {
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ p += outl;
+
+ if (p - (buf +sizeof(buf)/2) != sizeof(md5_password) + iv_len
+ || memcmp(buf + sizeof(buf)/2, (char *)(&md5_password) + key_len,
+ sizeof(md5_password)-key_len))
+ {
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+#if 0
+ {
+ int i;
+
+ printf("KEY: ");
+
+ for (i=0; i<key_len + iv_len; i++)
+ printf("%02X", (int)(unsigned char)(p-iv_len-key_len)[i]);
+ printf("\n");
+ }
+#endif
+
+ if (!EVP_DecryptInit_ex(&ctx, des, NULL,
+ (unsigned char *)(p-iv_len-key_len),
+ (unsigned char *)(p-iv_len)))
+ {
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ readinfo.nstrings=0;
+ readinfo.readhandler= &read_stringhi;
+ for (;;)
+ {
+ if (readinfo.bufleft == 0)
+ {
+ outl= (*readinfo.readfunc)(readinfo.buf,
+ sizeof(readinfo.buf),
+ readinfo.readfuncarg);
+
+ if (outl == 0)
+ break;
+
+ if (outl < 0)
+ {
+ tlspassword_readcleanup(&readinfo);
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ readinfo.bufptr=readinfo.buf;
+ readinfo.bufleft=outl;
+ }
+
+ if (!EVP_DecryptUpdate(&ctx, (unsigned char *)buf, &outl,
+ (unsigned char *)
+ readinfo.bufptr, readinfo.bufleft))
+ {
+ tlspassword_readcleanup(&readinfo);
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+ readinfo.bufleft=0;
+
+ p=buf;
+ while (outl)
+ {
+ int n= (*readinfo.readhandler)(&readinfo, p, outl);
+
+ if (n < 0)
+ {
+ tlspassword_readcleanup(&readinfo);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ p += n;
+ outl -= n;
+ }
+ }
+
+ if (!EVP_DecryptFinal_ex(&ctx, (unsigned char *)buf, &outl))
+ {
+ tlspassword_readcleanup(&readinfo);
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ p=buf;
+ while (outl)
+ {
+ int n= (*readinfo.readhandler)(&readinfo, p, outl);
+
+ if (n < 0)
+ {
+ tlspassword_readcleanup(&readinfo);
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return -1;
+ }
+
+ p += n;
+ outl -= n;
+ }
+
+ if (readinfo.tl_list && readinfo.tl_list->pw == NULL)
+ /* Odd # of strings -- no good */
+ {
+ tlspassword_readcleanup(&readinfo);
+ errno=EINVAL;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return (-1);
+ }
+
+ if ((urls=malloc((readinfo.nstrings+1) * sizeof(char *))) == NULL ||
+ (pws=malloc((readinfo.nstrings+1) * sizeof(char *))) == NULL)
+ {
+ if (urls)
+ free(urls);
+
+ tlspassword_readcleanup(&readinfo);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return (-1);
+ }
+
+ l=0;
+ for (tl=readinfo.tl_list; tl; tl=tl->next)
+ {
+ urls[l]=tl->url;
+ pws[l]=tl->pw;
+ l++;
+ }
+
+ urls[l]=NULL;
+ pws[l]=NULL;
+
+ (*readfunc)(urls, pws, readfunc_arg);
+
+ free(urls);
+ free(pws);
+
+ tlspassword_readcleanup(&readinfo);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+}
+
+static int read_stringlo(struct tlspassword_readinfo *info,
+ char *p, int n);
+
+static int read_string(struct tlspassword_readinfo *info,
+ char *p, int n);
+
+static int read_stringhi(struct tlspassword_readinfo *info,
+ char *p, int n)
+{
+ info->stringhi=(unsigned char)*p;
+ info->stringhi *= 256;
+
+ info->readhandler=read_stringlo;
+ return 1;
+}
+
+static int read_stringlo(struct tlspassword_readinfo *info,
+ char *p, int n)
+{
+ struct tempstring_list *t;
+
+ info->readhandler=read_string;
+ info->stringleft=info->stringhi + (unsigned char)*p;
+
+ if (info->tl_last &&
+ info->tl_last->pw == NULL) /* This string is the pw */
+ {
+ info->tl_last->pw=malloc(info->stringleft+1);
+ if (!info->tl_last->pw)
+ return -1;
+
+ info->stringptr=info->tl_last->pw;
+ return 1;
+ }
+
+ if ((t=(struct tempstring_list *)malloc(sizeof(struct tempstring_list))
+ ) == NULL || (t->url=malloc(info->stringleft+1)) == NULL)
+ {
+ if (t) free(t);
+ return -1;
+ }
+
+ if (info->tl_last)
+ info->tl_last->next=t;
+ else
+ info->tl_list=t;
+ info->tl_last=t;
+ info->stringptr=t->url;
+ t->next=NULL;
+ t->pw=NULL;
+ ++info->nstrings;
+ return 1;
+}
+
+static int read_string(struct tlspassword_readinfo *info, char *p, int n)
+{
+ if (n > info->stringleft)
+ n=info->stringleft;
+
+ memcpy(info->stringptr, p, n);
+ info->stringptr += n;
+ info->stringleft -= n;
+
+ if (info->stringleft == 0)
+ {
+ info->readhandler=read_stringhi;
+ *info->stringptr=0;
+ }
+
+ return n;
+}
+
+#else
+
+
+int tlspassword_init()
+{
+ return 0;
+}
+
+
+int tlspassword_save( const char * const *urls,
+ const char * const *pwds,
+ const char *mpw,
+ int (*writefunc)(const char *, size_t, void *),
+ void *writefuncarg)
+{
+ errno=EIO;
+ return -1;
+}
+
+int tlspassword_load( int (*readfunc)(char *, size_t, void *),
+ void *readfuncarg,
+
+ const char *mpw,
+ void (*callback)(const char * const *,
+ const char * const *,
+ void *),
+ void *callback_arg)
+{
+ errno=EIO;
+ return -1;
+}
+#endif