diff options
Diffstat (limited to 'libmailutils/secret.c')
-rw-r--r-- | libmailutils/secret.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/libmailutils/secret.c b/libmailutils/secret.c new file mode 100644 index 000000000..e472839cd --- /dev/null +++ b/libmailutils/secret.c @@ -0,0 +1,135 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2009, 2010 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <mailutils/types.h> +#include <mailutils/secret.h> +#include <mailutils/errno.h> + +struct _mu_secret +{ + unsigned int refcnt; /* Number of references to this object */ + size_t length; /* Secret length */ + unsigned char *obptr; /* Obfuscated data */ + unsigned char *clptr; /* Cleartext data */ + unsigned int clref; /* Number of references to clptr returned + this far */ +}; + +static unsigned char xchar; + +static void +obfuscate (const unsigned char *input, unsigned char *output, size_t len) +{ + if (!xchar) + xchar = random () % 255; + while (len--) + *output++ = *input++ ^ xchar; +} + +int +mu_secret_create (mu_secret_t *psec, const char *value, size_t len) +{ + mu_secret_t sec; + sec = calloc (1, sizeof (sec[0]) + 2 * (len + 1)); + if (!sec) + return ENOMEM; + sec->obptr = (unsigned char*)(sec + 1); + sec->clptr = sec->obptr + len + 1; + obfuscate ((unsigned char *) value, sec->obptr, len); + sec->length = len; + *psec = sec; + mu_secret_ref (sec); + return 0; +} + +int +mu_secret_dup (mu_secret_t sec, mu_secret_t *newsec) +{ + const char *pass = mu_secret_password (sec); + int rc = mu_secret_create (newsec, pass, strlen (pass)); + mu_secret_password_unref (sec); + return rc; +} + +void +mu_secret_ref (mu_secret_t sec) +{ + if (sec) + sec->refcnt++; +} + +/* Decrement reference counter in SEC. If it falls to 0, free memory + allocated for SEC and return 0. Otherwise, return MU_ERR_EXISTS, + indicating that someone else is still holding it. + Return EINVAL if sec==NULL. */ +int +mu_secret_unref (mu_secret_t sec) +{ + if (sec) + { + if (sec->refcnt) + sec->refcnt--; + if (sec->refcnt == 0) + { + memset (sec->clptr, 0, sec->length); + memset (sec->obptr, 0, sec->length); + free (sec); + return 0; + } + return MU_ERR_EXISTS; + } + return EINVAL; +} + +void +mu_secret_destroy (mu_secret_t *psec) +{ + if (psec && *psec && mu_secret_unref (*psec) == 0) + *psec = NULL; +} + +const char * +mu_secret_password (mu_secret_t sec) +{ + if (!sec) + return 0; + if (sec->clref++ == 0) + obfuscate (sec->obptr, sec->clptr, sec->length); + return (const char*) sec->clptr; +} + +size_t +mu_secret_length (mu_secret_t sec) +{ + if (!sec) + return 0; + return sec->length; +} + +void +mu_secret_password_unref (mu_secret_t sec) +{ + if (--sec->clref == 0) + memset (sec->clptr, 0, sec->length); +} + |