/*
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: misc.c 2357 2006-11-06 15:13:18Z leander $
*
* Miscelaneous functions */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "auth.h"
#include "dbmail.h"
#include "dbmd5.h"
#include "misc.h"
int drop_privileges(char *newuser, char *newgroup)
{
/* will drop running program's priviledges to newuser and newgroup */
struct passwd *pwd;
struct group *grp;
grp = getgrnam(newgroup);
if (grp == NULL) {
trace(TRACE_ERROR, "%s,%s: could not find group %s\n",
__FILE__, __func__, newgroup);
return -1;
}
pwd = getpwnam(newuser);
if (pwd == NULL) {
trace(TRACE_ERROR, "%s,%s: could not find user %s\n",
__FILE__, __func__, newuser);
return -1;
}
if (setgid(grp->gr_gid) != 0) {
trace(TRACE_ERROR, "%s,%s: could not set gid to %s\n",
__FILE__, __func__, newgroup);
return -1;
}
if (setuid(pwd->pw_uid) != 0) {
trace(TRACE_ERROR, "%s,%s: could not set uid to %s\n",
__FILE__, __func__, newuser);
return -1;
}
return 0;
}
char *itoa(int i)
{
char *s = (char *) dm_malloc(42); /* Enough for a 128 bit integer */
if (s)
sprintf(s, "%d", i);
return s;
}
void create_unique_id(char *target, u64_t message_idnr)
{
char *a_message_idnr, *a_rand;
char *md5_str;
a_message_idnr = itoa(message_idnr);
a_rand = itoa(rand());
if (message_idnr != 0)
snprintf(target, UID_SIZE, "%s:%s",
a_message_idnr, a_rand);
else
snprintf(target, UID_SIZE, "%s", a_rand);
md5_str = (char *)makemd5(target);
snprintf(target, UID_SIZE, "%s", md5_str);
trace(TRACE_DEBUG, "%s,%s: created: %s", __FILE__, __func__,
target);
dm_free(md5_str);
dm_free(a_message_idnr);
dm_free(a_rand);
}
void create_current_timestring(timestring_t * timestring)
{
time_t td;
struct tm tm;
if (time(&td) == -1)
trace(TRACE_FATAL, "%s,%s: error getting time from OS",
__FILE__, __func__);
tm = *localtime(&td); /* get components */
strftime((char *) timestring, sizeof(timestring_t),
"%Y-%m-%d %H:%M:%S", &tm);
}
char *mailbox_add_namespace(const char *mailbox_name, u64_t owner_idnr,
u64_t user_idnr)
{
char *fq_name;
char *owner_name;
size_t fq_name_len;
if (mailbox_name == NULL) {
trace(TRACE_ERROR, "%s,%s: error, mailbox_name is "
"NULL.", __FILE__, __func__);
return NULL;
}
if (user_idnr == owner_idnr) {
/* mailbox owned by current user */
return dm_strdup(mailbox_name);
} else {
owner_name = auth_get_userid(owner_idnr);
if (owner_name == NULL) {
trace(TRACE_ERROR,
"%s,%s: error owner_name is NULL", __FILE__,
__func__);
return NULL;
}
trace(TRACE_ERROR, "%s,%s: owner name = %s", __FILE__,
__func__, owner_name);
if (strcmp(owner_name, PUBLIC_FOLDER_USER) == 0) {
fq_name_len = strlen(NAMESPACE_PUBLIC) +
strlen(MAILBOX_SEPERATOR) +
strlen(mailbox_name) + 1;
if (!(fq_name = dm_malloc(fq_name_len *
sizeof(char)))) {
trace(TRACE_ERROR,
"%s,%s: not enough memory", __FILE__,
__func__);
dm_free(owner_name);
return NULL;
}
snprintf(fq_name, fq_name_len, "%s%s%s",
NAMESPACE_PUBLIC, MAILBOX_SEPERATOR,
mailbox_name);
} else {
fq_name_len = strlen(NAMESPACE_USER) +
strlen(MAILBOX_SEPERATOR) +
strlen(owner_name) +
strlen(MAILBOX_SEPERATOR) +
strlen(mailbox_name) + 1;
if (!(fq_name = dm_malloc(fq_name_len *
sizeof(char)))) {
trace(TRACE_ERROR,
"%s,%s: not enough memory", __FILE__,
__func__);
dm_free(owner_name);
return NULL;
}
snprintf(fq_name, fq_name_len, "%s%s%s%s%s",
NAMESPACE_USER, MAILBOX_SEPERATOR,
owner_name, MAILBOX_SEPERATOR,
mailbox_name);
}
dm_free(owner_name);
trace(TRACE_INFO, "%s,%s: returning fully qualified name "
"[%s]", __FILE__, __func__, fq_name);
return fq_name;
}
}
const char *mailbox_remove_namespace(const char *fq_name)
{
char *temp;
/* a lot of strlen() functions are used here, so this
can be quite inefficient! On the other hand, this
is a function that's not used that much. */
if (strcmp(fq_name, NAMESPACE_USER) == 0) {
temp = strstr(fq_name, MAILBOX_SEPERATOR);
if (temp == NULL || strlen(temp) <= 1) {
trace(TRACE_ERROR,
"%s,%s wronly constructed mailbox " "name",
__FILE__, __func__);
return NULL;
}
temp = strstr(&temp[1], MAILBOX_SEPERATOR);
if (temp == NULL || strlen(temp) <= 1) {
trace(TRACE_ERROR,
"%s,%s wronly constructed mailbox " "name",
__FILE__, __func__);
return NULL;
}
return &temp[1];
}
if (strcmp(fq_name, NAMESPACE_PUBLIC) == 0) {
temp = strstr(fq_name, MAILBOX_SEPERATOR);
if (temp == NULL || strlen(temp) <= 1) {
trace(TRACE_ERROR,
"%s,%s wronly constructed mailbox " "name",
__FILE__, __func__);
return NULL;
}
return &temp[1];
}
return fq_name;
}
int ci_write(FILE * fd, char * msg, ...)
{
va_list ap;
va_start(ap, msg);
if (feof(fd) || vfprintf(fd,msg,ap) < 0 || fflush(fd) < 0) {
va_end(ap);
return -1;
}
va_end(ap);
return 0;
}
/* This base64 code is heavily modified from fetchmail.
*
* Original copyright notice:
*
* The code in the fetchmail distribution is Copyright 1997 by Eric
* S. Raymond. Portions are also copyrighted by Carl Harris, 1993
* and 1995. Copyright retained for the purpose of protecting free
* redistribution of source.
* */
#define BAD -1
static const char base64val[] = {
BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
BAD, BAD, BAD,
BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD,
BAD, BAD, BAD,
BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 62, BAD,
BAD, BAD, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD, BAD, BAD, BAD, BAD,
BAD,
BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD, BAD, BAD, BAD,
BAD,
BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD, BAD, BAD, BAD, BAD
};
#define DECODE64(c) (isascii(c) ? base64val[c] : BAD)
/* Base64 to raw bytes in quasi-big-endian order */
/* Returns 0 on success, -1 on failure */
int base64_decode_internal(const char *in, size_t inlen, size_t maxlen,
char *out, size_t * outlen)
{
size_t pos = 0;
size_t len = 0;
register unsigned char digit1, digit2, digit3, digit4;
/* Don't even bother if the string is too short */
if (inlen < 4)
return -1;
do {
digit1 = in[0];
if (DECODE64(digit1) == BAD)
return -1;
digit2 = in[1];
if (DECODE64(digit2) == BAD)
return -1;
digit3 = in[2];
if (digit3 != '=' && DECODE64(digit3) == BAD)
return -1;
digit4 = in[3];
if (digit4 != '=' && DECODE64(digit4) == BAD)
return -1;
in += 4;
pos += 4;
++len;
if (maxlen && len > maxlen)
return -1;
*out++ = (DECODE64(digit1) << 2) | (DECODE64(digit2) >> 4);
if (digit3 != '=') {
++len;
if (maxlen && len > maxlen)
return -1;
*out++ =
((DECODE64(digit2) << 4) & 0xf0) |
(DECODE64(digit3) >> 2);
if (digit4 != '=') {
++len;
if (maxlen && len > maxlen)
return -1;
*out++ =
((DECODE64(digit3) << 6) & 0xc0) |
DECODE64(digit4);
}
}
} while (pos < inlen && digit4 != '=');
*out = '\0';
*outlen = len;
return 0;
}
/* A frontend to the base64_decode_internal() that deals with embedded strings */
char **base64_decode(char *str, size_t len)
{
size_t i, j, n, maxlen;
size_t numstrings = 0;
char *str_decoded = NULL;
size_t len_decoded = 0;
char **ret = NULL;
/* Base64 encoding required about 40% more space.
* So we'll allocate 50% more space. */
maxlen = 3 * len / 2;
str_decoded = (char *) dm_malloc(sizeof(char) * maxlen);
if (str_decoded == NULL)
return NULL;
if (0 !=
base64_decode_internal(str, len, maxlen, str_decoded,
&len_decoded))
return NULL;
if (str_decoded == NULL)
return NULL;
/* Count up the number of embedded strings... */
for (i = 0; i <= len_decoded; i++) {
if (str_decoded[i] == '\0') {
numstrings++;
}
}
/* Allocate an array of arrays large enough
* for the strings and a terminating NULL */
ret = (char **) dm_malloc(sizeof(char *) * (numstrings + 1));
if (ret == NULL)
return NULL;
/* If there are more strings, copy those, too */
for (i = j = n = 0; i <= len_decoded; i++) {
if (str_decoded[i] == '\0') {
ret[n] = dm_strdup(str_decoded + j);
j = i + 1;
n++;
}
}
/* Put that final NULL on the end of the array */
ret[n] = NULL;
dm_free(str_decoded);
return ret;
}
void base64_free(char **ret)
{
size_t i;
if (ret == NULL)
return;
for (i = 0; ret[i] != NULL; i++) {
dm_free(ret[i]);
}
dm_free(ret);
}
/* Return 0 is all's well. Returns something else if not... */
int read_from_stream(FILE * instream, char **m_buf, size_t maxlen)
{
size_t f_len = 0;
size_t f_pos = 0;
char *tmp_buf = NULL;
char *f_buf = NULL;
/* Give up on a zero length request */
if (maxlen < 1) {
*m_buf = NULL;
return 0;
}
tmp_buf = dm_malloc(sizeof(char) * (f_len += 512));
if (tmp_buf != NULL)
f_buf = tmp_buf;
else
return -2;
/* Shouldn't this also check for ferror() or feof() ?? */
while (f_pos < maxlen) {
if (f_pos + 1 >= f_len) {
/* Per suggestion of my CS instructor, double the
* buffer every time it is too small. This yields
* a logarithmic number of reallocations. */
tmp_buf =
dm_realloc(f_buf, sizeof(char) * (f_len *= 2));
if (tmp_buf != NULL)
f_buf = tmp_buf;
else
return -2;
}
f_buf[f_pos] = fgetc(instream);
f_pos++;
}
if (f_pos)
f_buf[f_pos] = '\0';
*m_buf = f_buf;
return 0;
}
/* Finds what lurks between two bounding symbols.
* Allocates and fills retchar with the string.
*
* Return values are:
* 0 on success (found and allocated)
* -1 on failure (not found)
* -2 on memory error (found but allocation failed)
*
* The caller is responsible for free()ing *retchar.
* */
int find_bounded(char *value, char left, char right, char **retchar,
size_t * retsize, size_t * retlast)
{
char *tmpleft;
char *tmpright;
size_t tmplen;
tmpleft = value;
tmpright = value + strlen(value);
while (tmpleft[0] != left && tmpleft < tmpright)
tmpleft++;
while (tmpright[0] != right && tmpright > tmpleft)
tmpright--;
if (tmpleft[0] != left || tmpright[0] != right) {
trace(TRACE_INFO,
"%s, %s: Found nothing between '%c' and '%c'",
__FILE__, __func__, left, right);
*retchar = NULL;
*retsize = 0;
*retlast = 0;
return -1;
} else {
/* Step left up to skip the actual left thinger */
if (tmpright != tmpleft)
tmpleft++;
tmplen = tmpright - tmpleft;
*retchar = dm_malloc(sizeof(char) * (tmplen + 1));
if (*retchar == NULL) {
*retchar = NULL;
*retsize = 0;
*retlast = 0;
trace(TRACE_INFO,
"%s, %s: Found [%s] of length [%zd] between '%c' and '%c' so next skip [%zd]",
__FILE__, __func__, *retchar, *retsize,
left, right, *retlast);
return -2;
}
strncpy(*retchar, tmpleft, tmplen);
(*retchar)[tmplen] = '\0';
*retsize = tmplen;
*retlast = tmpright - value;
trace(TRACE_INFO,
"%s, %s: Found [%s] of length [%zd] between '%c' and '%c' so next skip [%zd]",
__FILE__, __func__, *retchar, *retsize, left,
right, *retlast);
return 0;
}
}
/* Following the advice of:
* "Secure Programming for Linux and Unix HOWTO"
* Chapter 8: Carefully Call Out to Other Resources */
char *dm_shellesc(const char * command)
{
char *safe_command;
int pos, end, len;
// These are the potentially unsafe characters:
// & ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r
// # ! \t \ (space)
len = strlen(command);
if (!(safe_command = dm_calloc((len + 1) * 2 + 1, sizeof(char))))
return NULL;
for (pos = end = 0; pos < len; pos++) {
switch (command[pos]) {
case '&':
case ';':
case '`':
case '\'':
case '\\':
case '"':
case '|':
case '*':
case '?':
case '~':
case '<':
case '>':
case '^':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '$':
case '\n':
case '\r':
case '\t':
case ' ':
case '#':
case '!':
// Add an escape before the offending char.
safe_command[end++] = '\\';
default:
// And then put in the character itself.
safe_command[end++] = command[pos];
break;
}
}
/* The string is already initialized,
* but let's be extra double sure. */
safe_command[end] = '\0';
return safe_command;
}
syntax highlighted by Code2HTML, v. 0.9.1