/* * cdbutil.c * part of the vpopmail package * * Copyright (C) 1999,2001 Inter7 Internet Technologies, Inc. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /****************************************************************************** ** ** $Id: vmkcdb.c,v 1.1 1999/02/21 13:24:54 chris Exp $ ** Change a domain's password file to a CDB database ** ** Chris Johnson, July 1998 ** ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "vpopmail.h" #include "vauth.h" #include "vcdb.h" #include "file_lock.h" #define TOKENS " \n" char *dc_filename(char *domain, uid_t uid, gid_t gid); void vcdb_strip_char( char *instr ); extern int cdb_seek(); #define MAX_BUFF 300 static char vpasswd_file[MAX_BUFF]; static char vpasswd_bak_file[MAX_BUFF]; static char vpasswd_cdb_file[MAX_BUFF]; static char vpasswd_cdb_tmp_file[MAX_BUFF]; static char vpasswd_lock_file[MAX_BUFF]; static char vpasswd_dir[MAX_BUFF]; static char TmpBuf1[MAX_BUFF]; #define SMALL_BUFF 200 static char IUser[SMALL_BUFF]; static char IPass[SMALL_BUFF]; static char IGecos[SMALL_BUFF]; static char IDir[SMALL_BUFF]; static char IShell[SMALL_BUFF]; static char IClearPass[SMALL_BUFF]; int make_vpasswd_cdb(char *domain) { char pwline[256]; char packbuf[8]; char *key; char *data; char *ptr; int i,j,h; int len; long unsigned keylen,datalen; uint32 pos,op; struct cdbmake cdbm; FILE *pwfile, *tmfile; char Dir[156]; uid_t uid; gid_t gid; char *tmpstr; /* If we don't optimize the index this time, just return */ if ( NoMakeIndex == 1 ) return(0); if ((pwfile = fopen(vpasswd_file,"r")) == NULL) { return(-1); } cdbmake_init(&cdbm); if ((tmfile = fopen(vpasswd_cdb_tmp_file,"w")) == NULL) { fprintf(stderr,"Error: could not create/open temporary file\n"); return(-1); } for (i=0; i < sizeof(cdbm.final); i++) { if (putc(' ',tmfile) == EOF) { fprintf(stdout,"Error:error writing temp file\n"); return(-1); } } pos = sizeof(cdbm.final); /* creation **/ fgets(pwline,sizeof(pwline),pwfile); while (!feof(pwfile)) { key = pwline; ptr = pwline; while (*ptr != ':') { ptr++; } *ptr = 0; data = ptr; data++; while (*ptr != '\n') { ptr++; } *ptr = 0; keylen = strlen(key); datalen = strlen(data); #ifdef DEBUG fprintf (stdout,"Got entry: keylen = %lu, key = %s\n datalen = %lu, data = %s\n",keylen,key,datalen,data); #endif cdbmake_pack(packbuf, (uint32)keylen); cdbmake_pack(packbuf + 4, (uint32)datalen); if (fwrite(packbuf,1,8,tmfile) < 8) { fprintf(stderr,"Error: error writing temp file\n"); return(-1); } h = CDBMAKE_HASHSTART; for (i=0; i < keylen; i++) { h = cdbmake_hashadd(h,key[i]); if (putc(key[i],tmfile) == EOF) { fprintf (stderr,"Error: error temp file\n"); return(-1); } } for (i=0; i < datalen; i++) { if (putc(data[i],tmfile) == EOF) { fprintf (stderr,"Error: write error temp file"); return(-1); } } if (!cdbmake_add(&cdbm,h,pos,malloc)) { fprintf(stderr, "Error: out of memory\n"); return(-1); } op = pos; pos += (uint32)8; pos += (uint32)keylen; pos += (uint32)datalen; if (pos < op) { fprintf(stderr,"Error: too much data\n"); return(-1); } if (!cdbmake_split(&cdbm,malloc)) { fprintf(stderr,"Error: out of memory\n"); return(-1); } fgets(pwline,sizeof(pwline),pwfile); free(cdbm.split); } fclose(pwfile); if (!cdbmake_split(&cdbm,malloc)) { fprintf(stderr, "Error: out of memory\n"); return(-1); } for (i=0; i < 256; i++) { len = cdbmake_throw(&cdbm,pos,i); for (j=0; j < len; j++) { cdbmake_pack(packbuf,cdbm.hash[j].h); cdbmake_pack(packbuf + 4, cdbm.hash[j].p); if (fwrite(packbuf,1,8,tmfile) < 8) { fprintf(stderr,"Error 1: error temp file\n"); return(-1); } op = pos; pos += (uint32)8; if (pos < op) { fprintf (stdout, "Error 12: too much data\n"); return(-1); } } } if (fflush(tmfile) == EOF) { fprintf (stderr,"Error 20: write error temp file\n"); return(-1); } rewind(tmfile); if (fwrite(cdbm.final,1,sizeof(cdbm.final),tmfile) MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG); if ( strlen(username) == 1 ) return(VA_ILLEGAL_USERNAME); if ( strlen(domain) > MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); if ( strlen(quota) > MAX_PW_QUOTA ) return(VA_QUOTA_TOO_LONG); vpw = vauth_getpw( username, domain ); if ( vpw==NULL ) return(VA_USER_DOES_NOT_EXIST); vpw->pw_shell = quota; return(vauth_setpw(vpw,domain)); } int vauth_setpw( struct vqpasswd *inpw, char *domain ) { static char tmpbuf1[MAX_BUFF]; static char tmpbuf2[MAX_BUFF]; char *tmpstr; FILE *fs1; FILE *fs2; #ifdef FILE_LOCKING FILE *fs3; #endif uid_t myuid; uid_t uid; gid_t gid; int ret; ret = vcheck_vqpw(inpw, domain); if ( ret != 0 ) return(ret); /* get the owner of the domain */ vget_assign(domain,NULL,156,&uid,&gid); /* get the current effective user */ myuid = geteuid(); /* * if it is not the owner, vpopmail or root * then reject this operation */ if ( myuid != 0 && myuid != uid ) { return(VA_BAD_UID); } set_vpasswd_files( domain ); #ifdef FILE_LOCKING fs3 = fopen(vpasswd_lock_file, "w+"); if ( get_write_lock(fs3) < 0 ) return(-2); #endif fs1 = fopen(vpasswd_bak_file, "w+"); if ( (fs2 = fopen(vpasswd_file, "r+")) == NULL ) { fs2 = fopen(vpasswd_file, "w+"); } if ( fs1 == NULL || fs2 == NULL ) { if ( fs1 != NULL ) fclose(fs1); if ( fs2 != NULL ) fclose(fs2); #ifdef FILE_LOCKING unlock_lock(fileno(fs3), 0, SEEK_SET, 0); fclose(fs3); #endif return(-1); } vcdb_strip_char( inpw->pw_gecos ); #ifndef CLEAR_PASS vcdb_strip_char( inpw->pw_clear_passwd ); #endif while (fgets(tmpbuf1,MAX_BUFF,fs2)!=NULL){ strncpy(tmpbuf2, tmpbuf1, MAX_BUFF); tmpstr = strtok(tmpbuf2,":\n"); if ( strcmp(inpw->pw_name, tmpstr) != 0) { fputs(tmpbuf1, fs1); } else { #ifndef CLEAR_PASS fprintf(fs1, "%s:%s:%d:%d:%s:%s:%s\n", inpw->pw_name, inpw->pw_passwd, inpw->pw_uid, inpw->pw_gid, inpw->pw_gecos, inpw->pw_dir, inpw->pw_shell); #else fprintf(fs1, "%s:%s:%d:%d:%s:%s:%s:%s\n", inpw->pw_name, inpw->pw_passwd, inpw->pw_uid, inpw->pw_gid, inpw->pw_gecos, inpw->pw_dir, inpw->pw_shell, inpw->pw_clear_passwd); #endif } } fclose(fs1); fclose(fs2); rename(vpasswd_bak_file, vpasswd_file); make_vpasswd_cdb(domain); #ifdef FILE_LOCKING unlock_lock(fileno(fs3), 0, SEEK_SET, 0); fclose(fs3); #endif #ifdef SQWEBMAIL_PASS tmpstr = vget_assign(domain, NULL, 156, &uid, &gid ); vsqwebmail_pass( inpw->pw_dir, inpw->pw_passwd, uid, gid); #endif return(0); } int vauth_adduser_line( FILE *fs1, char *user, char *pass, char *domain, char *gecos, char *dir, int apop ) { char Dir[156]; uid_t uid; gid_t gid; char crypted[100]; if ( vget_assign(domain, Dir, 156, &uid, &gid ) == NULL ) { strcpy(Dir, VPOPMAILDIR); } if ( pass[0] != 0 ) { mkpasswd3(pass,crypted, 100); } else { crypted[0] = 0; } fprintf(fs1,"%s:", user ); if ( apop == USE_POP ) fprintf(fs1, "%s:1:", crypted); else fprintf(fs1, "%s:2:", crypted); fprintf(fs1, "0:%s:%s", gecos, Dir); if ( strlen(domain) <= 0 ) { if ( strlen(dir) > 0 ) { fprintf(fs1, "/users/%s/%s:", dir, user); } else { fprintf(fs1, "/users/%s:", user); } } else { if ( strlen(dir) > 0 ) { fprintf(fs1,"/%s/%s:", dir,user); } else { fprintf(fs1, "/%s:", user); } } #ifdef HARD_QUOTA fprintf(fs1, "%s", HARD_QUOTA); #else fprintf(fs1, "NOQUOTA"); #endif #ifndef CLEAR_PASS fprintf(fs1, "\n"); #else fprintf(fs1, ":%s\n", pass); #endif return(0); } int vmkpasswd( char *domain ) { #ifdef FILE_LOCKING FILE *fs3; #endif char Dir[156]; uid_t uid; gid_t gid; char *tmpstr; getcwd(TmpBuf1, MAX_BUFF); tmpstr = vget_assign(domain, Dir, 156, &uid, &gid ); if ( chdir(Dir) != 0 ) return(VA_BAD_DIR); lowerit(domain); set_vpasswd_files( domain ); #ifdef FILE_LOCKING fs3 = fopen(vpasswd_lock_file, "w+"); if ( get_write_lock(fs3) < 0 ) return(-2); #endif make_vpasswd_cdb(domain); #ifdef FILE_LOCKING unlock_lock(fileno(fs3), 0, SEEK_SET, 0); fclose(fs3); #endif return(0); } void vclose() { } #ifdef IP_ALIAS_DOMAINS int vget_ip_map( char *ip, char *domain, int domain_size) { FILE *fs; char tmpbuf[156]; char *tmpstr; if ( ip == NULL || strlen(ip) <= 0 ) return(-1); /* open the ip_alias_map file */ snprintf(tmpbuf, 156, "%s/%s", VPOPMAILDIR, IP_ALIAS_MAP_FILE); if ( (fs = fopen(tmpbuf,"r")) == NULL ) return(-1); while( fgets(tmpbuf, 156, fs) != NULL ) { tmpstr = strtok(tmpbuf, IP_ALIAS_TOKENS); if ( tmpstr == NULL ) continue; if ( strcmp(ip, tmpstr) != 0 ) continue; tmpstr = strtok(NULL, IP_ALIAS_TOKENS); if ( tmpstr == NULL ) continue; strncpy(domain, tmpstr, domain_size); fclose(fs); return(0); } fclose(fs); return(-1); } /* * Add an ip to domain mapping * It will remove any duplicate entry before adding it * */ int vadd_ip_map( char *ip, char *domain) { FILE *fs; char tmpbuf[156]; if ( ip == NULL || strlen(ip) <= 0 ) return(-1); if ( domain == NULL || strlen(domain) <= 0 ) return(-10); vdel_ip_map( ip, domain ); snprintf(tmpbuf, 156, "%s/%s", VPOPMAILDIR, IP_ALIAS_MAP_FILE); if ( (fs = fopen(tmpbuf,"a+")) == NULL ) return(-1); fprintf( fs, "%s %s\n", ip, domain); fclose(fs); return(0); } int vdel_ip_map( char *ip, char *domain) { FILE *fs; FILE *fs1; char file1[156]; char file2[156]; char tmpbuf[156]; char tmpbuf1[156]; char *ip_f; char *domain_f; if ( ip == NULL || strlen(ip) <= 0 ) return(-1); if ( domain == NULL || strlen(domain) <= 0 ) return(-1); snprintf(file1, 156, "%s/%s", VPOPMAILDIR, IP_ALIAS_MAP_FILE); if ( (fs = fopen(file1,"r")) == NULL ) return(-1); snprintf(file2, 156, "%s/%s.%d", VPOPMAILDIR, IP_ALIAS_MAP_FILE, getpid()); if ( (fs1 = fopen(file2,"w")) == NULL ) { fclose(fs); return(-1); } while( fgets(tmpbuf, 156, fs) != NULL ) { strncpy(tmpbuf1,tmpbuf, 156); ip_f = strtok(tmpbuf, IP_ALIAS_TOKENS); if ( ip_f == NULL ) continue; domain_f = strtok(NULL, IP_ALIAS_TOKENS); if ( domain_f == NULL ) continue; if ( strcmp(ip, ip_f) == 0 && strcmp(domain,domain_f) == 0) continue; fprintf(fs1, tmpbuf1); } fclose(fs); fclose(fs1); if ( rename( file2, file1) < 0 ) return(-1); return(0); } int vshow_ip_map( int first, char *ip, char *domain) { static FILE *fs = NULL; char tmpbuf[156]; char *tmpstr; if ( ip == NULL ) return(-1); if ( domain == NULL ) return(-1); if ( first == 1 ) { if ( fs != NULL ) { fclose(fs); fs = NULL; } snprintf(tmpbuf, 156, "%s/%s", VPOPMAILDIR, IP_ALIAS_MAP_FILE); if ( (fs = fopen(tmpbuf,"r")) == NULL ) return(-1); } if ( fs == NULL ) return(-1); while (1) { if (fgets(tmpbuf, 156, fs) == NULL ) { fclose(fs); fs = NULL; return(0); } tmpstr = strtok(tmpbuf, IP_ALIAS_TOKENS); if ( tmpstr == NULL ) continue; strcpy( ip, tmpstr); tmpstr = strtok(NULL, IP_ALIAS_TOKENS); if ( tmpstr == NULL ) continue; strcpy( domain, tmpstr); return(1); } return(-1); } #endif int vread_dir_control(vdir_type *vdir, char *domain, uid_t uid, gid_t gid) { FILE *fs; char dir_control_file[MAX_DIR_NAME]; int i; strncpy(dir_control_file,dc_filename(domain, uid, gid),MAX_DIR_NAME); if ( (fs = fopen(dir_control_file, "r")) == NULL ) { vdir->cur_users = 0; for(i=0;ilevel_start[i] = 0; vdir->level_end[i] = MAX_DIR_LIST-1; vdir->level_index[i] = 0; } vdir->level_mod[0] = 0; vdir->level_mod[1] = 2; vdir->level_mod[2] = 4; vdir->level_cur = 0; vdir->level_max = MAX_DIR_LEVELS; vdir->the_dir[0] = 0; return(-1); } fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->cur_users = atol(dir_control_file); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_cur = atoi(dir_control_file); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_max = atoi(dir_control_file); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_start[0] = atoi(dir_control_file); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_start[1] = atoi(&dir_control_file[i]); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_start[2] = atoi(&dir_control_file[i]); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_end[0] = atoi(dir_control_file); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_end[1] = atoi(&dir_control_file[i]); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_end[2] = atoi(&dir_control_file[i]); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_mod[0] = atoi(dir_control_file); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_mod[1] = atoi(&dir_control_file[i]); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_mod[2] = atoi(&dir_control_file[i]); fgets(dir_control_file, MAX_DIR_NAME, fs ); vdir->level_index[0] = atoi(dir_control_file); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_index[1] = atoi(&dir_control_file[i]); for(i=0;dir_control_file[i]!=' ';++i); ++i; vdir->level_index[2] = atoi(&dir_control_file[i]); fgets(dir_control_file, MAX_DIR_NAME, fs ); for(i=0;dir_control_file[i]!=0;++i) { if (dir_control_file[i] == '\n') { dir_control_file[i] = 0; } } fgets(dir_control_file, MAX_DIR_NAME, fs ); for(i=0;dir_control_file[i]!=0;++i) { if (dir_control_file[i] == '\n') { dir_control_file[i] = 0; } } strncpy(vdir->the_dir, dir_control_file, MAX_DIR_NAME); fclose(fs); return(0); } int vwrite_dir_control(vdir_type *vdir, char *domain, uid_t uid, gid_t gid) { FILE *fs; char dir_control_file[MAX_DIR_NAME]; char dir_control_tmp_file[MAX_DIR_NAME]; strncpy(dir_control_file,dc_filename(domain, uid, gid),MAX_DIR_NAME); snprintf(dir_control_tmp_file, MAX_DIR_NAME, "%s.%d", dir_control_file, getpid()); if ( (fs = fopen(dir_control_tmp_file, "w+")) == NULL ) { return(-1); } fprintf(fs, "%lu\n", vdir->cur_users); fprintf(fs, "%d\n", vdir->level_cur); fprintf(fs, "%d\n", vdir->level_max); fprintf(fs, "%d %d %d\n", vdir->level_start[0], vdir->level_start[1], vdir->level_start[2]); fprintf(fs, "%d %d %d\n", vdir->level_end[0], vdir->level_end[1], vdir->level_end[2]); fprintf(fs, "%d %d %d\n", vdir->level_mod[0], vdir->level_mod[1], vdir->level_mod[2]); fprintf(fs, "%d %d %d\n", vdir->level_index[0], vdir->level_index[1], vdir->level_index[2]); fprintf(fs, "%s\n", vdir->the_dir); fclose(fs); rename( dir_control_tmp_file, dir_control_file); chown(dir_control_file,uid, gid); return(0); } int vdel_dir_control(char *domain) { char dir_control_file[MAX_DIR_NAME]; vget_assign(domain, dir_control_file, 156, NULL,NULL); strncat(dir_control_file,"/.dir-control", MAX_DIR_NAME); return(unlink(dir_control_file)); } int vset_lastauth(char *user, char *domain, char *remoteip ) { #ifdef ENABLE_AUTH_LOGGING char *tmpbuf; FILE *fs; struct vqpasswd *vpw; uid_t uid; gid_t gid; vpw = vauth_getpw( user, domain ); tmpbuf = malloc(MAX_BUFF); snprintf(tmpbuf, MAX_BUFF, "%s/lastauth", vpw->pw_dir); if ( (fs = fopen(tmpbuf,"w+")) == NULL ) { free(tmpbuf); return(-1); } fprintf(fs, "%s", remoteip); fclose(fs); vget_assign(domain,NULL,0,&uid,&gid); chown(tmpbuf,uid,gid); free(tmpbuf); #endif return(0); } time_t vget_lastauth( struct vqpasswd *pw, char *domain) { #ifdef ENABLE_AUTH_LOGGING char *tmpbuf; struct stat mystatbuf; tmpbuf = malloc(MAX_BUFF); snprintf(tmpbuf, MAX_BUFF, "%s/lastauth", pw->pw_dir); if ( stat(tmpbuf,&mystatbuf) == -1 ) { free(tmpbuf); return(0); } free(tmpbuf); return(mystatbuf.st_mtime); #else return(0); #endif } char *vget_lastauthip( struct vqpasswd *pw, char *domain) { #ifdef ENABLE_AUTH_LOGGING static char tmpbuf[MAX_BUFF]; FILE *fs; snprintf(tmpbuf, MAX_BUFF, "%s/lastauth", pw->pw_dir); if ( (fs=fopen(tmpbuf,"r"))==NULL) return(NULL); fgets(tmpbuf,MAX_BUFF,fs); fclose(fs); return(tmpbuf); #else return(NULL); #endif } char *dc_filename(char *domain, uid_t uid, gid_t gid) { static char dir_control_file[MAX_DIR_NAME]; struct passwd *pw; /* if we are lucky the domain is in the assign file */ if ( vget_assign(domain,dir_control_file,MAX_DIR_NAME,NULL,NULL)!=NULL ) { strncat(dir_control_file, "/.dir-control", MAX_DIR_NAME); /* it isn't in the assign file so we have to get it from /etc/passwd */ } else { /* save some time if this is the vpopmail user */ if ( uid == VPOPMAILUID ) { strncpy(dir_control_file, VPOPMAILDIR, MAX_DIR_NAME); /* for other users, look them up in /etc/passwd */ } else if ( (pw=getpwuid(uid))!=NULL ) { strncpy(dir_control_file, pw->pw_dir, MAX_DIR_NAME); /* all else fails return a blank string */ } else { return(""); } /* stick on the rest of the path */ strncat(dir_control_file, "/" DOMAINS_DIR "/.dir-control", MAX_DIR_NAME); } return(dir_control_file); } void vcdb_strip_char( char *instr ) { char *nextstr; nextstr = instr; while (*instr != 0 ) { if ( *instr == ':' ) ++instr; if ( nextstr != instr ) *nextstr = *instr; ++nextstr; ++instr; } }