Index: ChangeLog =================================================================== RCS file: /cvsroot/vpopmail/vpopmail/ChangeLog,v retrieving revision 1.103.2.22 retrieving revision 1.103.2.31 diff -u -r1.103.2.22 -r1.103.2.31 --- ChangeLog 1 Jul 2004 05:09:59 -0000 1.103.2.22 +++ ChangeLog 27 Aug 2004 17:57:44 -0000 1.103.2.31 @@ -1,17 +1,35 @@ Numbers in square brackets ([]) are tracker items on SourceForge with patch or info related to the entry. +5.4.7 - unreleased + + Tom Collins + - Don't try to delete dir-control for domain unless users-big-dir + is enabled. + - Verify user exists before trying to set quota in vsetuserquota(). + [984698] + - Update cdb/Makefile so you can 'make install' without doing + 'make' first. + - Fix size comparisons to MAX_PW_X (should be ">", not ">="). + - Fix possible buffer overflows in vsybase.c. + - Have vconvert reset dir_control and increment it for each user + added when converting from cdb to MySQL. + - If crypt() doesn't support MD5 passwords, fall back to using + a valid, non-MD5 salt even if MD5 passwords are enabled. + 5.4.6 - released 30-Jun-04 [backport from 5.5.0] - Consolidate table creation code in vmysql.c and vpgsql.c. - Increase SQL_BUF_SIZE from 600 to 2048 for Oracle, Postgres and Sybase. + - Multiple fixes to vpgsql.c related to freeing PGresults and + attempting to access NULL PGresults when reporting errors. + * These changes address SQL Injection vulnerability documented in + * Bugtraq ID 10990 - Add qnprintf() to vpopmail.c for escaping strings in SQL queries. - Use qnprintf() when building queries in vmysql.c, vpgsql.c, voracle.pc, and vsybase.c. - - Multiple fixes to vpgsql.c related to freeing PGresults and - attempting to access NULL PGresults when reporting errors. 5.4.5 - released 25-Jun-04 Index: vconvert.c =================================================================== RCS file: /cvsroot/vpopmail/vpopmail/vconvert.c,v retrieving revision 1.2.2.1 retrieving revision 1.2.2.2 diff -u -r1.2.2.1 -r1.2.2.2 --- vconvert.c 10 Mar 2004 15:18:50 -0000 1.2.2.1 +++ vconvert.c 24 Aug 2004 17:17:39 -0000 1.2.2.2 @@ -1,5 +1,5 @@ /* - * $Id: vconvert.c,v 1.2.2.1 2004/03/10 15:18:50 tomcollins Exp $ + * $Id: vconvert.c,v 1.2.2.2 2004/08/24 17:17:39 tomcollins Exp $ * Copyright (C) 1999-2002 Inter7 Internet Technologies, Inc. * * This program is free software; you can redistribute it and/or modify @@ -188,6 +188,8 @@ int i, colon_count, dir_count; int bFoundDomain = 0; char assign_file[MAX_BUFF]; + uid_t uid; + gid_t gid; snprintf(assign_file, sizeof(assign_file), "%s/users/assign", QMAILDIR); if ( (assign_fs=fopen(assign_file, "r"))==NULL ) { @@ -227,9 +229,13 @@ fclose(assign_fs); vauth_deldomain(domain); + vdel_dir_control(domain); vauth_adddomain(domain); - vget_assign(domain, Dir, sizeof(Dir), NULL, NULL ); + vget_assign(domain, Dir, sizeof(Dir), &uid, &gid ); +#ifdef USERS_BIG_DIR + open_big_dir (domain, uid, gid); +#endif snprintf(tmpbuf, sizeof(tmpbuf), "%s/vpasswd", Dir); fs = fopen(tmpbuf,"r"); if ( fs == NULL ) return(-1); @@ -241,8 +247,14 @@ continue; } vauth_setpw(pw, domain); +#ifdef USERS_BIG_DIR + next_big_dir (uid, gid); /* increment user count */ +#endif } fclose(fs); +#ifdef USERS_BIG_DIR + close_big_dir (domain, uid, gid); +#endif #endif /* USE_SQL */ return(0); } Index: vpalias.c =================================================================== RCS file: /cvsroot/vpopmail/vpopmail/vpalias.c,v retrieving revision 1.6 retrieving revision 1.6.2.1 diff -u -r1.6 -r1.6.2.1 --- vpalias.c 14 Jan 2004 23:55:21 -0000 1.6 +++ vpalias.c 19 Aug 2004 05:42:34 -0000 1.6.2.1 @@ -1,6 +1,6 @@ #ifndef VALIAS /* - * $Id: vpalias.c,v 1.6 2004/01/14 23:55:21 tomcollins Exp $ + * $Id: vpalias.c,v 1.6.2.1 2004/08/19 05:42:34 tomcollins Exp $ * Copyright (C) 2000-2002 Inter7 Internet Technologies, Inc. * * This program is free software; you can redistribute it and/or modify @@ -59,12 +59,12 @@ return( NULL ); } - if ( strlen(alias) >= MAX_PW_NAME ) { + if ( strlen(alias) > MAX_PW_NAME ) { verrori = VA_USER_NAME_TOO_LONG; return( NULL ); } - if ( strlen(domain) >= MAX_PW_DOMAIN ) { + if ( strlen(domain) > MAX_PW_DOMAIN ) { verrori = VA_DOMAIN_NAME_TOO_LONG; return( NULL ); } @@ -116,8 +116,8 @@ if ( alias == NULL ) return(VA_NULL_POINTER); if ( domain == NULL ) return(VA_NULL_POINTER); if ( alias_line == NULL ) return(VA_NULL_POINTER); - if ( strlen(alias) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(alias) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); if ( strlen(alias_line) >= MAX_ALIAS_LINE ) return(VA_ALIAS_LINE_TOO_LONG); if ((tmpstr = vget_assign(domain, Dir, sizeof(Dir), &uid, &gid )) == NULL) { @@ -155,8 +155,8 @@ if ( alias == NULL ) return(VA_NULL_POINTER); if ( domain == NULL ) return(VA_NULL_POINTER); - if ( strlen(alias) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(alias) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); if ((tmpstr = vget_assign(domain, Dir, 156, &uid, &gid )) == NULL) { printf("invalid domain, not in qmail assign file\n"); @@ -183,7 +183,7 @@ return( NULL ); } - if ( strlen(domain) >= MAX_PW_DOMAIN ) { + if ( strlen(domain) > MAX_PW_DOMAIN ) { verrori = VA_DOMAIN_NAME_TOO_LONG; return( NULL ); } Index: vpopmail.c =================================================================== RCS file: /cvsroot/vpopmail/vpopmail/vpopmail.c,v retrieving revision 1.28.2.4 retrieving revision 1.28.2.8 diff -u -r1.28.2.4 -r1.28.2.8 --- vpopmail.c 26 Jun 2004 02:20:56 -0000 1.28.2.4 +++ vpopmail.c 27 Aug 2004 17:57:45 -0000 1.28.2.8 @@ -1,5 +1,5 @@ /* - * $Id: vpopmail.c,v 1.28.2.4 2004/06/26 02:20:56 tomcollins Exp $ + * $Id: vpopmail.c,v 1.28.2.8 2004/08/27 17:57:45 tomcollins Exp $ * Copyright (C) 2000-2002 Inter7 Internet Technologies, Inc. * * This program is free software; you can redistribute it and/or modify @@ -101,7 +101,7 @@ if ( strlen( domain) <3) return (VA_INVALID_DOMAIN_NAME); /* reject domain names that exceed our max permitted/storable size */ - if ( strlen( domain ) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen( domain ) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); /* check invalid email domain characters */ for(i=0;domain[i]!=0;++i) { @@ -174,7 +174,7 @@ * We dont want to start creating dirs and putting entries in * the assign file etc if the path is going to be too long */ - if (strlen(dir)+strlen(DOMAINS_DIR)+strlen(DomainSubDir) >= MAX_PW_DIR) { + if (strlen(dir)+strlen(DOMAINS_DIR)+strlen(DomainSubDir) > MAX_PW_DIR) { /* back out of changes made so far */ dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); @@ -262,9 +262,11 @@ fprintf(stderr, "Failed while attempting to delete domain from the qmail control files\n"); } +#ifdef USERS_BIG_DIR if (vdel_dir_control(domain) != 0) { - fprintf (stderr, "Failed while attempting to delete domain from dir_control\n"); + fprintf (stderr, "Warning: Failed to delete dir_control for %s\n", domain); } +#endif /* send a HUP signal to qmail-send process to reread control files */ signal_process("qmail-send", SIGHUP); @@ -311,7 +313,7 @@ * asking to del that domain, because such a domain * wouldnt be able to exist in the 1st place */ - if (strlen(domain) >= MAX_PW_DOMAIN) return (VA_DOMAIN_NAME_TOO_LONG); + if (strlen(domain) > MAX_PW_DOMAIN) return (VA_DOMAIN_NAME_TOO_LONG); /* now we want to check a couple for things : * a) if the domain to del exists in the system @@ -379,10 +381,12 @@ */ vdel_limits(domain); +#ifdef USERS_BIG_DIR /* delete the dir control info for this domain */ if (vdel_dir_control(domain) != 0) { fprintf (stderr, "Warning: Failed to delete dir_control for %s\n", domain); } +#endif /* Now remove domain from filesystem */ /* if it's a symbolic link just remove the link */ @@ -447,15 +451,15 @@ /* check gecos for : characters - bad */ if ( strchr(gecos,':')!=0) return(VA_BAD_CHAR); - if ( strlen(username) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(username) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); #ifdef USERS_BIG_DIR if ( strlen(username) == 1 ) return(VA_ILLEGAL_USERNAME); #endif - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); if ( strlen(domain) < 3) return(VA_INVALID_DOMAIN_NAME); - if ( strlen(password) >= MAX_PW_CLEAR_PASSWD ) return(VA_PASSWD_TOO_LONG); - if ( strlen(gecos) >= MAX_PW_GECOS ) return(VA_GECOS_TOO_LONG); + if ( strlen(password) > MAX_PW_CLEAR_PASSWD ) return(VA_PASSWD_TOO_LONG); + if ( strlen(gecos) > MAX_PW_GECOS ) return(VA_GECOS_TOO_LONG); umask(VPOPMAIL_UMASK); lowerit(username); @@ -602,6 +606,19 @@ tmpstr = crypt(clearpass,salt); if ( tmpstr == NULL ) return(VA_CRYPT_FAILED); +#ifdef MD5_PASSWORDS + /* Make sure this host's crypt supports MD5 passwords. If not, + * fall back on old-style crypt + */ + if (tmpstr[2] != '$') { + salt[0] = randltr(); + salt[1] = randltr(); + salt[2] = 0; + tmpstr = crypt(clearpass,salt); + if ( tmpstr == NULL ) return(VA_CRYPT_FAILED); + } +#endif + strncpy(crypted,tmpstr, ssize); return(VA_SUCCESS); } @@ -1321,12 +1338,12 @@ gid_t gid; #endif - if ( strlen(username) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(username) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); #ifdef USERS_BIG_DIR if ( strlen(username) == 1 ) return(VA_ILLEGAL_USERNAME); #endif - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); - if ( strlen(password) >= MAX_PW_CLEAR_PASSWD ) return(VA_PASSWD_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(password) > MAX_PW_CLEAR_PASSWD ) return(VA_PASSWD_TOO_LONG); lowerit(username); lowerit(domain); @@ -1546,16 +1563,19 @@ char *formattedquota; int ret; - if ( strlen(username) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(username) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); #ifdef USERS_BIG_DIR if ( strlen(username) == 1 ) return(VA_ILLEGAL_USERNAME); #endif - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); - if ( strlen(quota) >= MAX_PW_QUOTA ) return(VA_QUOTA_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(quota) > MAX_PW_QUOTA ) return(VA_QUOTA_TOO_LONG); lowerit(username); lowerit(domain); + mypw = vauth_getpw( username, domain ); + if (mypw == NULL) return VA_USER_DOES_NOT_EXIST; + /* correctly format the quota string, * and then store the quota into the auth backend */ @@ -1563,7 +1583,6 @@ ret = vauth_setquota( username, domain, formattedquota); if (ret != VA_SUCCESS ) return(ret); - mypw = vauth_getpw( username, domain ); remove_maildirsize(mypw->pw_dir); if (strcmp (quota, "NOQUOTA") != 0) { uid_t uid; @@ -1723,7 +1742,7 @@ #endif /* check the length of the dir path to make sure it is not too long to save back to the auth backend */ - if ((strlen(domain_dir)+strlen(user_hash)+strlen(username)) >= MAX_PW_DIR) { + if ((strlen(domain_dir)+strlen(user_hash)+strlen(username)) > MAX_PW_DIR) { fprintf (stderr, "Error. Path exceeds maximum permitted length\n"); chdir(calling_dir); return (NULL); @@ -1764,11 +1783,11 @@ if ( mypw != NULL ) { /* user does exist in the auth backend, so fill in the dir field */ - mypw->pw_dir = malloc(MAX_PW_DIR); + mypw->pw_dir = malloc(MAX_PW_DIR+1); if ( strlen(user_hash) > 0 ) { - snprintf(mypw->pw_dir, MAX_PW_DIR, "%s/%s/%s", domain_dir, user_hash, username); + snprintf(mypw->pw_dir, MAX_PW_DIR+1, "%s/%s/%s", domain_dir, user_hash, username); } else { - snprintf(mypw->pw_dir, MAX_PW_DIR, "%s/%s", domain_dir, username); + snprintf(mypw->pw_dir, MAX_PW_DIR+1, "%s/%s", domain_dir, username); } /* save these values to the auth backend */ vauth_setpw( mypw, domain ); @@ -1857,7 +1876,7 @@ char *default_domain() { static int init = 0; - static char d[MAX_PW_DOMAIN]; + static char d[MAX_PW_DOMAIN+1]; char path[MAX_BUFF]; int dlen; FILE *fs; @@ -1909,8 +1928,9 @@ /* Michael Bowe 14th August 2003 * How can we prevent possible buffer overflows here * For the moment, stick with a conservative size of MAX_PW_DOMAIN + * (plus 1 for the NULL) */ - snprintf(domain, MAX_PW_DOMAIN, "%s", tmpstr); + snprintf(domain, MAX_PW_DOMAIN+1, "%s", tmpstr); return; } @@ -1945,8 +1965,9 @@ /* Michael Bowe 14th August 2003 * How can we prevent possible buffer overflows here * For the moment, stick with a conservative size of MAX_PW_DOMAIN + * (plus 1 for the NULL) */ - snprintf(domain, MAX_PW_DOMAIN, "%s", host); + snprintf(domain, MAX_PW_DOMAIN+1, "%s", host); } return; } @@ -1955,8 +1976,9 @@ /* Michael Bowe 14th August 2003 * How can we prevent possible buffer overflows here * For the moment, stick with a conservative size of MAX_PW_DOMAIN + * (plus 1 for the NULL) */ - snprintf(domain, MAX_PW_DOMAIN, "%s", DEFAULT_DOMAIN); + snprintf(domain, MAX_PW_DOMAIN+1, "%s", DEFAULT_DOMAIN); } /************************************************************************/ @@ -2282,10 +2304,11 @@ * * Michael Bowe 21st Aug 2003. Need to watch out for buffer overflows here. * We dont know what size domain is, so stick with a conservative limit of MAX_PW_DOMAIN + * (plus 1 for the NULL) * Not sure if this is our best option? the pw entry shouldnt contain any dirs larger * than this. */ - snprintf(domain, MAX_PW_DOMAIN, "%s", in_domain); + snprintf(domain, MAX_PW_DOMAIN+1, "%s", in_domain); } else { free(in_domain); @@ -2786,15 +2809,15 @@ /* when checking for excess size using strlen, the check needs use >= because you * have to allow 1 char for null termination */ - if ( strlen(inpw->pw_name) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); + if ( strlen(inpw->pw_name) > MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); if ( strlen(inpw->pw_name) == 1 ) return(VA_ILLEGAL_USERNAME); - if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); - if ( strlen(inpw->pw_passwd) >= MAX_PW_PASS ) return(VA_PASSWD_TOO_LONG); - if ( strlen(inpw->pw_gecos) >= MAX_PW_GECOS ) return(VA_GECOS_TOO_LONG); - if ( strlen(inpw->pw_dir) >= MAX_PW_DIR ) return(VA_DIR_TOO_LONG); - if ( strlen(inpw->pw_shell) >= MAX_PW_QUOTA ) return(VA_QUOTA_TOO_LONG); + if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); + if ( strlen(inpw->pw_passwd) > MAX_PW_PASS ) return(VA_PASSWD_TOO_LONG); + if ( strlen(inpw->pw_gecos) > MAX_PW_GECOS ) return(VA_GECOS_TOO_LONG); + if ( strlen(inpw->pw_dir) > MAX_PW_DIR ) return(VA_DIR_TOO_LONG); + if ( strlen(inpw->pw_shell) > MAX_PW_QUOTA ) return(VA_QUOTA_TOO_LONG); #ifdef CLEAR_PASS - if ( strlen(inpw->pw_clear_passwd) >= MAX_PW_CLEAR_PASSWD ) + if ( strlen(inpw->pw_clear_passwd) > MAX_PW_CLEAR_PASSWD ) return(VA_CLEAR_PASSWD_TOO_LONG); #endif return(VA_SUCCESS); @@ -2928,7 +2951,7 @@ if ( (err=is_domain_valid(alias_domain)) != VA_SUCCESS ) return(err); /* make sure the alias domain does not exceed the max storable size */ - if (strlen(alias_domain) >= MAX_PW_DOMAIN) { + if (strlen(alias_domain) > MAX_PW_DOMAIN) { return(VA_DOMAIN_NAME_TOO_LONG); } Index: vsybase.c =================================================================== RCS file: /cvsroot/vpopmail/vpopmail/vsybase.c,v retrieving revision 1.9.2.1 retrieving revision 1.9.2.2 diff -u -r1.9.2.1 -r1.9.2.2 --- vsybase.c 26 Jun 2004 02:20:56 -0000 1.9.2.1 +++ vsybase.c 19 Aug 2004 16:32:35 -0000 1.9.2.2 @@ -1,5 +1,5 @@ /* - * $Id: vsybase.c,v 1.9.2.1 2004/06/26 02:20:56 tomcollins Exp $ + * $Id: vsybase.c,v 1.9.2.2 2004/08/19 16:32:35 tomcollins Exp $ * Copyright (C) 1999-2003 Inter7 Internet Technologies, Inc. * * This program is free software; you can redistribute it and/or modify @@ -101,7 +101,7 @@ if ( dbuse(dbproc, SYBASE_DATABASE) == FAIL ) { dbcancel(dbproc); - sprintf( SqlBuf, "create database %s", SYBASE_DATABASE ); + snprintf( SqlBuf, sizeof(SqlBuf), "create database %s", SYBASE_DATABASE ); dbcmd(dbproc, SqlBuf); dbsqlexec(dbproc); while(dbresults(dbproc) != NO_MORE_RESULTS) @@ -126,10 +126,10 @@ if ( site_size == LARGE_SITE ) { tmpstr = vauth_munch_domain( domain ); - sprintf( SqlBuf1, "create table %s ( %s )", + snprintf( SqlBuf1, sizeof (SqlBuf1), "create table %s ( %s )", tmpstr, LARGE_TABLE_LAYOUT ); } else { - sprintf( SqlBuf1, "create table %s ( %s )", + snprintf( SqlBuf1, sizeof (SqlBuf1), "create table %s ( %s )", SYBASE_DEFAULT_TABLE, SMALL_TABLE_LAYOUT); } @@ -175,16 +175,16 @@ if ( strlen(domain) <= 0 ) { if ( strlen(dir) > 0 ) { - sprintf(dirbuf, "%s/users/%s/%s", VPOPMAILDIR, dir, user); + snprintf(dirbuf, sizeof(dirbuf), "%s/users/%s/%s", VPOPMAILDIR, dir, user); } else { - sprintf(dirbuf, "%s/users/%s", VPOPMAILDIR, user); + snprintf(dirbuf, sizeof(dirbuf), "%s/users/%s", VPOPMAILDIR, user); } } else { vget_assign(domain, dom_dir, 156, &uid, &gid ); if ( strlen(dir) > 0 ) { - sprintf(dirbuf,"%s/%s/%s", dom_dir,dir,user); + snprintf(dirbuf, sizeof(dirbuf), "%s/%s/%s", dom_dir, dir, user); } else { - sprintf(dirbuf, "%s/%s", dom_dir, user); + snprintf(dirbuf, sizeof(dirbuf), "%s/%s", dom_dir, user); } } @@ -294,7 +294,7 @@ tmpstr = vauth_munch_domain( domain ); if ( site_size == LARGE_SITE ) { - sprintf( SqlBuf, "drop table %s", tmpstr); + snprintf( SqlBuf, sizeof(SqlBuf), "drop table %s", tmpstr); } else { qnprintf( SqlBuf, sizeof(SqlBuf), "delete from %s where pw_domain = '%s'", SYBASE_DEFAULT_TABLE, domain );