#============================================================================# # vhost - one-step solution for all virtual hosting needs # # V22majordomo - majordomo configuration module # # # # Copyright(c) Chaogic Systems, LLC. http://chaogic.com # # Author: Jake Fan # # # # This is Free Software; permission to use, copy, modify, and distribute # # this software and its documentation for any purpose - with or without fee # # - is hereby granted, provided that the above copyright information and # # this permission notice appear in all copies and documentations. This # # software is provided "as is" without express or implied warranty. For # # more details, see GNU General Public License as published by the Free # # Software Foundation. # # # #============================================================================# $X eq "configuration" && do { ################################################ ### EDITABLE CONFIGURATIONS ################################################## ### majordomo directory name on virtual hosts. will be created under "var/". # $majorDir = "majordomo"; $majorDir = "majordomo"; ### majordomo root directory on the main host. contains "majordomo" and other # executables. # $majorRoot = "/usr/local/$majorDir"; $majorRoot = "/usr/local/$majorDir"; ### majordomo configuration file on the main host. # $majorCf = "$majorRoot/majordomo.cf"; $majorCf = "$majorRoot/majordomo.cf"; ### the command-line mail utility. # $mailbin = "/bin/mail"; $mailbin = "/bin/mail"; ### mail aliases to be created on a virtual host when adding a mailing list. # vhost will replace "[HOST_NAME]", "[MAILING_LIST]", "[LIST_DIR]", and # "[DIGEST_DIR]" with actual values. variables found in "./V00hostconf" # will also be expanded. # $majorC = <<"---eoc---eoc---eoc---"; $majorC = <<"---eoc---eoc---eoc---"; [MAILING_LIST]: "| $VHOME/[HOST_NAME]/var/$majorDir/wrapper resend -l [MAILING_LIST] [MAILING_LIST]-list@[HOST_NAME]" [MAILING_LIST]-list: :include:$VHOME/[HOST_NAME]/var/$majorDir/[LIST_DIR]/[MAILING_LIST] [MAILING_LIST]-request: "| $VHOME/[HOST_NAME]/var/$majorDir/wrapper request-answer [MAILING_LIST]" [MAILING_LIST]-approval: $ADMIN [MAILING_LIST]-owner: $ADMIN owner-[MAILING_LIST]: $ADMIN ---eoc---eoc---eoc--- ### DO NOT TOUCH ANYTHING BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING!! ### SanityChecking ########################################################### my $help = <<'#'; Mailing List: vhost --addlist host_name mailing_list [-f] vhost --dellist host_name mailing_list [-f [-d]] # $HELP =~ s/^(\s*Manage User:)/$help$1/m; if ($ARGV[0] =~ /^(addlist|dellist)$/ && !$ERR) { undef $HLP; $H = $ARGV[1]; $M = $ARGV[2]; $ERR = 1; $ERR = 0 if $#ARGV+1 > 2 && $#ARGV-1 < 3 && join ("\n", @ARGV) =~ /^addlist\n[^-].*\n[^-].*(\n-[f\s-]+)?$/; $ERR = 0 if $#ARGV+1 > 2 && $#ARGV-1 < 4 && join ("\n", @ARGV) =~ /^dellist\n[^-].*\n[^-].*(\n-[fd\s-]+)?$/; $ERR && print STDERR $HELP; $ERR && exit 2; $ERR = !CheckH ($H, 1) || $ERR if length $H; $ERR = !CheckU ($M, 1) || $ERR if length $M; $ERR && exit 2; } if (!$ERR) { my ($ver, @ver); @ver = split /r/, (split /\s+/, $VERSION)[1]; $ver = $ver[0] * 10000 + $ver[1], $ver = "$ver"; $ERR = 1, Error "$MOD: requires vhost 3.21r1 or higher" if $ver < 32101; $ERR && exit 1; } $ERR = 1, Error "$MOD: \$majorDir = '$majorDir'" if !CheckN $majorDir; $ERR = 1, Error "$MOD: \$majorRoot = '$majorRoot'" if !-x "$majorRoot/majordomo"; $ERR = 1, Error "$MOD: \$majorCf = '$majorCf'" if !-f $majorCf; $ERR = 1, Error "$MOD: \$mailbin = '$mailbin'" if $mailbin !~ /^\// || !-x (split /[\s|&;]+/, $mailbin)[0]; $mlRoot = "$VHOME/$H/var/$majorDir"; }; $X eq "initialization" && do { ############################################### Security 2 if $ARGV[0] eq "addlist"; Security 2 if $ARGV[0] eq "dellist"; @M = getpwnam $M; $mailingCf = "/etc/vdata/mailing.$H"; @MLST = Cat $mailingCf; $MLST[-$_] =~ s/^(\s*,.*)//s && ($MLST[-$_-1] .= $1) for 1..$#MLST+1; /^\s*([^#:]+):\s*(.*)/s && ($MLST{$1} ||= $2) for @MLST; if ($ONCFG) { /^\s*\$whoami[\s="']+([^\s="'\\@;]+)/ && ($majorUser = lc $1 || "majordomo"), /^\s*\$whoami_owner[\s="']+([^\s="'\\@;]+)/ && ($majorOwner = lc $1 || "majordomo-owner"), /^\s*\$listdir[\s="']+([^\s="';]+)/ && ($majorList = (split /\//, lc $1)[-1] || "lists"), /^\s*\$digest_work_dir[\s="']+([^\s="';]+)/ && ($majorDigest = (split /\//, lc $1)[-1] || "digest"), /^\s*\$log[\s="']+([^\s="';]+)/ && ($majorLog = (split /\//, lc $1)[-1] || "log"), /^\s*\$filedir_suffix[\s="']+([^\s="';]+)/ && ($majorArchive = lc $1 || ".archive") for Cat -f "$mlRoot/majordomo.cf" && "$mlRoot/majordomo.cf" || $majorCf; $majorC =~ s/\[HOST_NAME\]/$H/g; $majorC =~ s/\[MAILING_LIST\]/$M/g; $majorC =~ s/\[LIST_DIR\]/$majorList/g; $majorC =~ s/\[DIGEST_DIR\]/$majorDigest/g; @majorC = split /^/m, $majorC; $majorC[-$_] =~ s/^(\s*,.*)//s && ($majorC[-$_-1] .= $1) for 1..$#majorC+1; /^\s*([^#:]+):\s*(.*)/s && ($majorC{$1} ||= $2) for @majorC; if (!-e "$majorRoot/wrapper.vhost") { my $ok; my $f = "gcc"; $ok = $ok || -f "$_/$f" && -x "$_/$f" for split /:/, $ENV{PATH}; $ERR = 1, Error "missing system command '$f'" if $f !~ /^\// && !$ok; $ERR && exit 1; my $ok; Flock "$majorRoot/wrapper.c", ">", 0; s/^=wrapper\.c .*//s && ($ok = 1), $ok && print FILE for Cat $MOD; Flock 0; `(cd $majorRoot && gcc -DBIN=\\"$majorRoot\\" -DHOME=\\"HOME=$majorRoot\\" -o wrapper.vhost wrapper.c) $NERR`; `rm $majorRoot/wrapper.c $NERR`; } } if ($ARGV[0] =~ /^addlist$/) { my $count = grep /^\s*[^#:]+:\s*:include:/i, @MLST; my $quota = (split /\s+/, $QHOST{$H})[2]; $ERR = 1, Error "$H: quota exceeded - user alias max $quota" if $count >= $quota && $quota && !$MLST{$M}; $ERR && Exit 4; } if ($ARGV[0] =~ /^(addlist|dellist)$/) { Error5 "'$H' is not virtual" if !$QHOST{$H}; Error5 "'$M' is a system user" if $M =~ /^(\Q$majorUser\E|\Q$majorOwner\E)$/; } sub MajorAdd { my ($i, $j, $k); `touch $mailingCf $NERR`; `mkdir $mlRoot $NERR`; `mkdir $mlRoot/$majorList $NERR`; `mkdir $mlRoot/$majorDigest $NERR`; `touch $mlRoot/$majorLog $NERR`; `cp -p $majorRoot/wrapper.vhost $mlRoot/wrapper $NERR`; `cp -p $majorCf $mlRoot/majordomo.cf $NERR`; `ln -f $mlRoot/majordomo.cf $VHOME/$H/etc/majordomo.cf $NERR`; Flock "$mlRoot/majordomo.cf", "+<", 0; s/^\s*(\$whereami\s*=).*/$1 "$H";\n/s, s/^\s*(\$whoami\s*=).*/$1 "$majorUser\\\@\$whereami";\n/s, s/^\s*(\$whoami_owner\s*=).*/$1 "$majorOwner\\\@\$whereami";\n/s, s/^\s*(\$listdir\s*=).*/$1 "$mlRoot\/$majorList";\n/s, s/^\s*(\$digest_work_dir\s*=).*/$1 "$mlRoot\/$majorDigest";\n/s, s/^\s*(\$log\s*=).*/$1 "$mlRoot\/$majorLog";\n/s, s/^\s*(\$filedir\s*=).*/$1 "\$listdir";\n/s, s/^\s*(\$filedir_suffix\s*=).*/$1 "$majorArchive";\n/s for @FILE; print FILE @FILE; Flock 0; Flock $mailingCf, "+<", 0; $FILE[-$_] =~ s/^(\s*,.*)//s && ($FILE[-$_-1] .= $1) for 1..$#FILE+1; s/^\s*\Q$majorUser\E:.*/$majorUser: "| $mlRoot\/wrapper majordomo"\n/is, s/^\s*\Q$majorUser\E-owner:.*/$majorUser-owner: $ADMIN\n/is, s/^\s*owner-\Q$majorUser\E:.*/owner-$majorUser: $ADMIN\n/is, s/^\s*\Q$majorOwner\E:.*/$majorOwner: $ADMIN\n/is for @FILE; print FILE @FILE; print FILE "$majorUser: \"| $mlRoot/wrapper majordomo\"\n" if !$MLST{$majorUser}; print FILE "$majorUser-owner: $ADMIN\n" if !$MLST{"$majorUser-owner"}; print FILE "owner-$majorUser: $ADMIN\n" if !$MLST{"owner-$majorUser"}; print FILE "$majorOwner: $ADMIN\n" if !$MLST{$majorOwner} && $majorOwner !~ /^(\Q$majorUser\E-owner|owner-\Q$majorUser\E)$/; Flock 0; Flock $aliasesCf, "+<", 0; $FILE[-$_] =~ s/^(\s*,.*)//s && ($FILE[-$_-1] .= $1) for 1..$#FILE+1; s/^\s*(\Q$majorUser\E:)/# $1/i, s/^\s*(\Q$majorUser\E-owner:)/# $1/i, s/^\s*(owner-\Q$majorUser\E:)/# $1/i, s/^\s*(\Q$majorOwner\E:)/# $1/i for @FILE; print FILE @FILE; Flock 0; Flock "/etc/vdeliver", "+<", 0; /^\s*vdomain(\.index\s+|_\w+\.)\Q$H\E(\s|$)/ && ($k = $i), /^\s*\[*vdomain[_.\]]/ && ($j = $i), $i++ for @FILE; $FILE[$k||$j||$i] .= "vdomain_alias.$H $mailingCf\n" if !grep /^\s*vdomain_alias\.\Q$H\E\s+\Q$mailingCf\E\s*$/, @FILE; print FILE @FILE; Flock 0; } MajorAdd if $ARGV[0] =~ /^(addlist|dellist)$/ && !-e $mlRoot; }; $X eq "addhost" && do { ###################################################### MajorAdd; }; $X eq "addlist" && do { ###################################################### Error5 "'$M".($H&&'@')."$H' already exists" if $MLST{$M} && !$_f; Error "warning - '$M".($H&&'@')."$H' already exists as an alias", 1 if $VALI{$M} && !$MLST{$M}; Error "warning - '$M".($H&&'@')."$H' already exists as a user", 1 if ($LOH && @M || $VPWD{$M}) && !$VALI{$M} && !$MLST{$M}; $LOCAL = 1, Rexec "vhost --addlist $H $M $_f", "HOST", $HOSTs if !$LOCAL; `touch $mlRoot/$majorList/$M $NERR` if !-e "$mlRoot/$majorList/$M"; `touch $mlRoot/$majorList/$M.info $NERR` if !-e "$mlRoot/$majorList/$M.info"; `mkdir $mlRoot/$majorList/$M$majorArchive $NERR`; `mkdir $mlRoot/$majorDigest/$M $NERR`; `chown $O[2]:$GUID $mlRoot/$majorList/$M $NERR`; `chown $O[2]:$GUID $mlRoot/$majorList/$M.info $NERR`; `chown -R $O[2]:$GUID $mlRoot/$majorList/$M$majorArchive $NERR`; `chown -R $O[2]:$GUID $mlRoot/$majorDigest/$M $NERR`; Flock $mailingCf, "+<", 0; $FILE[-$_] =~ s/^(\s*,.*)//s && ($FILE[-$_-1] .= $1) for 1..$#FILE+1; /^\s*([^#:]+):/ && $majorC{$1} && ($_ = "$1: $majorC{$1}") for @FILE; print FILE @FILE; /^\s*([^#:]+):/ && !$MLST{$1} && print FILE "$1: $majorC{$1}" for @majorC; Flock 0; $< = getpwnam $USER; `(echo config $M $M.admin | $mailbin $majorUser\@$H) $NERR`; $< = 0; }; $X eq "dellist" && do { ###################################################### Error5 "'$M".($H&&'@')."$H' is an alias" if $VALI{$M} && !$MLST{$M}; Error5 "'$M".($H&&'@')."$H' is a user" if ($LOH && @M || $VPWD{$M}) && !$MLST{$M}; Error5 "'$M".($H&&'@')."$H' does not exist" if !$MLST{$M}; Prompt "deleting '$M".($H&&'@')."$H'"; Forced "delete all list data"; $LOCAL = 1, Rexec "vhost --dellist $H $M $_f $_d", "HOST", $HOSTs if !$LOCAL; Flock $mailingCf, "+<", 0; $FILE[-$_] =~ s/^(\s*,.*)//s && ($FILE[-$_-1] .= $1) for 1..$#FILE+1; /^\s*([^#:]+):/ && $majorC{$1} && ($_ = "") for @FILE; print FILE @FILE; Flock 0; if ($_d) { `rm $mlRoot/$majorList/$M $NERR`; `rm $mlRoot/$majorList/$M.info $NERR`; `rm $mlRoot/$majorList/$M.config $NERR`; `rm -r $mlRoot/$majorList/$M$majorArchive $NERR`; `rm -r $mlRoot/$majorDigest/$M $NERR`; } }; $X eq "lsuser" && do { ####################################################### my ($m, $l, $ml, $msort, $mnum, $mcmp, $mmya, $mmyb, @ml); $mnum = (0, 0, 1, 2, 3, 4, 1, 6, 7, 8, 9, 10)[$_n<11?$_n:11]; $mmya = "(split /[\\/\\s]+/, \$a, 2)[$mnum]", $mmyb = "(split /[\\/\\s]+/, \$b, 2)[$mnum]"; $mcmp = $_n =~ /^[2]$/? "<=>" : "cmp"; $msort = ($_r && "reverse ") . "sort {$mmya $mcmp $mmyb} " if "$_s$_r$_n" && $mnum < 2; for ($H && @MLST) { /^\s*([^#:]+)-list:\s*:include:\s*(\S+)/i && ($m = $1, $l = $2) || next; $ml[$ml++] = sprintf "%s %d\n", $m, scalar grep /^\s*[^#\s]+/, Cat $l; } eval "\@MLIST = $msort \@ml;" if $H; }; $X eq "finalization" && do { ################################################# if ($ARGV[0] eq "delhost" && $_d) { `rm $mailingCf $NERR`; } if ($ARGV[0] eq "addhost" || $ARGV[0] =~ /^(addlist|dellist)$/) { `chown -R $O[2]:$GUID $mlRoot $NERR` if (stat $mlRoot)[4] != $O[2] || (stat $mlRoot)[5] != $GUID; `chown 0:$GUID $mlRoot/wrapper $NERR` if !-o "$mlRoot/wrapper" || (stat "$mlRoot/wrapper")[5] != $GUID; `chmod 4755 $mlRoot/wrapper $NERR` if ((stat "$mlRoot/wrapper")[2] & 07777) != 04755; } if ($ARGV[0] eq "lsuser") { printf "# - mailing list total %d\n", $#MLIST+1 if $H; print @MLIST if $H; } }; 1; =wrapper.c ################################################################### /* * + MAJORDOMO_CF made dynamic for virtual hosting * + setuid/setgid to the uid/gid of MAJORDOMO_CF */ /* * $Source: /sources/cvsrepos/majordomo/wrapper.c,v $ * $Revision: 1.8 $ * $Date: 1997/08/27 15:01:12 $ * $Author: cwilson $ * $State: Exp $ * * $Locker: $ * */ #ifndef lint static char rcs_header[] = "$Header: /sources/cvsrepos/majordomo/wrapper.c,v 1.8 1997/08/27 15:01:12 cwilson Exp $"; #endif #include #include #if defined(sun) && defined(sparc) #include #endif #ifndef STRCHR # include # define STRCHR(s,c) strchr(s,c) #endif #ifndef BIN # define BIN "/usr/local/mail/majordomo" #endif #ifndef PATH # define PATH "PATH=/bin:/usr/bin:/usr/ucb" #endif #ifndef HOME # define HOME "HOME=/usr/local/mail/majordomo" #endif #ifndef SHELL # define SHELL "SHELL=/bin/sh" #endif char * new_env[] = { HOME, /* 0 */ PATH, /* 1 */ SHELL, /* 2 */ #ifdef MAJORDOMO_CF MAJORDOMO_CF, /* 3 */ #endif 0, /* possibly for USER or LOGNAME */ 0, /* possible for LOGNAME */ 0, /* possibly for timezone */ 0 }; int new_env_size = 7; /* to prevent overflow problems */ #include #include main(argc, argv, env) int argc; char * argv[]; char * env[]; { char * prog; int e, i; char cf[strlen(argv[0])+256]; struct stat st; *(strrchr(strcat(strcpy(cf, ""), argv[0]), '/')) = '\0'; if (stat(cf, &st)) st.st_uid = st.st_gid = 2000; *(strrchr(strcat(strcpy(cf, "MAJORDOMO_CF="), argv[0]), '/')) = '\0'; new_env[3] = strcat(cf, "/majordomo.cf"); #define MAJORDOMO_CF cf #define POSIX_UID st.st_uid #define POSIX_GID st.st_gid if (argc < 2) { fprintf(stderr, "USAGE: %s program [ ...]\n", argv[0]); exit(EX_USAGE); } /* if the command contains a /, then don't allow it */ if (STRCHR(argv[1], '/') != (char *) NULL) { /* this error message is intentionally cryptic */ fprintf(stderr, "%s: error: insecure usage\n", argv[0]); exit(EX_NOPERM); } if ((prog = (char *) malloc(strlen(BIN) + strlen(argv[1]) + 2)) == NULL) { fprintf(stderr, "%s: error: malloc failed\n", argv[0]); exit(EX_OSERR); } sprintf(prog, "%s/%s", BIN, argv[1]); /* copy the "USER=" and "LOGNAME=" envariables into the new environment, * if they exist. */ #ifdef MAJORDOMO_CF e = 4; /* the first unused slot in new_env[] */ #else e = 3; /* the first unused slot in new_env[] */ #endif for (i = 0 ; env[i] != NULL && e <= new_env_size; i++) { if ((strncmp(env[i], "USER=", 5) == 0) || (strncmp(env[i], "TZ=", 3) == 0) || (strncmp(env[i], "LOGNAME=", 8) == 0)) { new_env[e++] = env[i]; } } #if defined(SETGROUP) /* renounce any previous group memberships if we are running as root */ if (geteuid() == 0) { /* Should I exit if this test fails? */ char *setgroups_used = "setgroups_was_included"; /* give strings a hint */ #if defined(MAIL_GID) int groups[] = { POSIX_GID, MAIL_GID, 0 }; if (setgroups(2, groups) == -1) { #else int groups[] = { POSIX_GID, 0 }; if (setgroups(1, groups) == -1) { #endif extern int errno; fprintf(stderr, "%s: error setgroups failed errno %d", argv[0], errno); } } #endif #ifdef POSIX_GID setgid(POSIX_GID); #else setgid(getegid()); #endif #ifdef POSIX_UID setuid(POSIX_UID); #else setuid(geteuid()); #endif if ((getuid() != geteuid()) || (getgid() != getegid())) { fprintf(stderr, "%s: error: Not running with proper UID and GID.\n", argv[0]); fprintf(stderr, " Make certain that wrapper is installed setuid, and if so,\n"); fprintf(stderr, " recompile with POSIX flags.\n"); exit(EX_SOFTWARE); } execve(prog, argv+1, new_env); /* the exec should never return */ fprintf(stderr, "wrapper: Trying to exec %s failed: ", prog); perror(NULL); fprintf(stderr, " Did you define PERL correctly in the Makefile?\n"); fprintf(stderr, " HOME is %s,\n", HOME); fprintf(stderr, " PATH is %s,\n", PATH); fprintf(stderr, " SHELL is %s,\n", SHELL); fprintf(stderr, " MAJORDOMO_CF is %s\n", MAJORDOMO_CF); exit(EX_OSERR); }