3 # $Id: svc_acct.export,v 1.36 2002-05-16 14:28:35 ivan Exp $
5 # Create and export password, radius and vpopmail password files:
6 # passwd, passwd.adjunct, shadow, acp_passwd, acp_userinfo, acp_dialup
7 # users/assign, domains/vdomain/vpasswd
8 # Also export sendmail and qmail config files.
18 use FS::UID qw(adminsuidsetup datasrc dbh);
19 use FS::Record qw(qsearch qsearchs fields);
27 my $user = shift or die &usage;
32 my $userpolicy = $conf->config('username_policy')
33 if $conf->exists('username_policy');
35 my @shellmachines = $conf->config('shellmachines')
36 if $conf->exists('shellmachines');
38 my @bsdshellmachines = $conf->config('bsdshellmachines')
39 if $conf->exists('bsdshellmachines');
41 my @nismachines = $conf->config('nismachines')
42 if $conf->exists('nismachines');
44 my @erpcdmachines = $conf->config('erpcdmachines')
45 if $conf->exists('erpcdmachines');
47 my @radiusmachines = $conf->config('radiusmachines')
48 if $conf->exists('radiusmachines');
50 my $textradiusprepend =
51 $conf->exists('textradiusprepend')
52 ? $conf->config('textradiusprepend')
55 warn "using depriciated textradiusprepend file" if $textradiusprepend;
59 $conf->exists('radiusprepend')
60 ? join("\n", $conf->config('radiusprepend'))
63 my @vpopmailmachines = $conf->config('vpopmailmachines')
64 if $conf->exists('vpopmailmachines');
65 my $vpopmailrestart = '';
66 $vpopmailrestart = $conf->config('vpopmailrestart')
67 if $conf->exists('vpopmailrestart');
69 my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachines[0]) if $vpopmailmachines[0];
71 my($shellmachine, @qmailmachines);
72 if ( $conf->exists('qmailmachines') ) {
73 $shellmachine = $conf->config('shellmachine');
74 @qmailmachines = $conf->config('qmailmachines');
77 my(@sendmailmachines, $sendmailconfigpath, $sendmailrestart);
78 if ( $conf->exists('sendmailmachines') ) {
79 @sendmailmachines = $conf->config('sendmailmachines');
80 $sendmailconfigpath = $conf->config('sendmailconfigpath') || '/etc';
81 $sendmailrestart = $conf->config('sendmailrestart');
84 my $mydomain = $conf->config('domain') if $conf->exists('domain');
89 my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
90 require 5.004; #srand(time|$$);
92 my $spooldir = "/usr/local/etc/freeside/export.". datasrc;
93 my $spoollock = "/usr/local/etc/freeside/svc_acct.export.lock.". datasrc;
95 open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!";
96 select(EXPORT); $|=1; select(STDOUT);
97 unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) {
101 #no reason to start lots of blocking processes
102 die "Is another export process running under pid $pid?\n";
105 print EXPORT $$,"\n";
107 my(@svc_domain)=qsearch('svc_domain',{});
109 ( open(MASTER,">$spooldir/master.passwd")
110 and flock(MASTER,LOCK_EX|LOCK_NB)
111 ) or die "Can't open $spooldir/.master.passwd: $!";
112 ( open(PASSWD,">$spooldir/passwd")
113 and flock(PASSWD,LOCK_EX|LOCK_NB)
114 ) or die "Can't open $spooldir/passwd: $!";
115 ( open(SHADOW,">$spooldir/shadow")
116 and flock(SHADOW,LOCK_EX|LOCK_NB)
117 ) or die "Can't open $spooldir/shadow: $!";
118 ( open(ACP_PASSWD,">$spooldir/acp_passwd")
119 and flock(ACP_PASSWD,LOCK_EX|LOCK_NB)
120 ) or die "Can't open $spooldir/acp_passwd: $!";
121 ( open(ACP_DIALUP,">$spooldir/acp_dialup")
122 and flock(ACP_DIALUP,LOCK_EX|LOCK_NB)
123 ) or die "Can't open $spooldir/acp_dialup: $!";
124 ( open(USERS,">$spooldir/users")
125 and flock(USERS,LOCK_EX|LOCK_NB)
126 ) or die "Can't open $spooldir/users: $!";
128 ( open(ASSIGN,">$spooldir/assign")
129 and flock(ASSIGN,LOCK_EX|LOCK_NB)
130 ) or die "Can't open $spooldir/assign: $!";
131 ( open(RCPTHOSTS,">$spooldir/rcpthosts")
132 and flock(RCPTHOSTS,LOCK_EX|LOCK_NB)
133 ) or die "Can't open $spooldir/rcpthosts: $!";
134 ( open(VPOPRCPTHOSTS,">$spooldir/vpoprcpthosts")
135 and flock(VPOPRCPTHOSTS,LOCK_EX|LOCK_NB)
136 ) or die "Can't open $spooldir/rcpthosts: $!";
137 ( open(RECIPIENTMAP,">$spooldir/recipientmap")
138 and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB)
139 ) or die "Can't open $spooldir/recipientmap: $!";
140 ( open(VIRTUALDOMAINS,">$spooldir/virtualdomains")
141 and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB)
142 ) or die "Can't open $spooldir/virtualdomains: $!";
143 ( open(VPOPVIRTUALDOMAINS,">$spooldir/vpopvirtualdomains")
144 and flock(VPOPVIRTUALDOMAINS,LOCK_EX|LOCK_NB)
145 ) or die "Can't open $spooldir/virtualdomains: $!";
146 ( open(VIRTUSERTABLE,">$spooldir/virtusertable")
147 and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB)
148 ) or die "Can't open $spooldir/virtusertable: $!";
149 ( open(SENDMAIL_CW,">$spooldir/sendmail.cw")
150 and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB)
151 ) or die "Can't open $spooldir/sendmail.cw: $!";
155 chmod 0644, "$spooldir/passwd",
156 "$spooldir/acp_dialup",
158 "$spooldir/sendmail.cw",
159 "$spooldir/virtusertable",
160 "$spooldir/rcpthosts",
161 "$spooldir/vpoprcpthosts",
162 "$spooldir/recipientmap",
163 "$spooldir/virtualdomains",
164 "$spooldir/vpopvirtualdomains",
167 chmod 0600, "$spooldir/master.passwd",
168 "$spooldir/acp_passwd",
173 rmtree"$spooldir/domains", 0, 1;
174 mkdir "$spooldir/domains", 0700;
178 print USERS "$radiusprepend\n";
180 my %usernames; ## this hack helps keep the passwd files sane
184 foreach $svc_domain (sort {$a->domain cmp $b->domain} @svc_domain) {
186 my($domain)=$svc_domain->domain;
187 print RCPTHOSTS "$domain\n.$domain\n";
188 print VPOPRCPTHOSTS "$domain\n";
189 print SENDMAIL_CW "$domain\n";
192 # FORMAT OF THE ASSIGN/USERS FILE HERE
193 print ASSIGN join(":",
198 $vpopdir . "/domains/" . $domain,
202 ), "\n" if $vpopmailmachines[0];
204 (mkdir "$spooldir/domains/" . $domain, 0700)
205 or die "Can't create $spooldir/domains/" . $domain .": $!";
207 ( open(QMAILDEFAULT,">$spooldir/domains/" . $domain . "/.qmail-default")
208 and flock(QMAILDEFAULT,LOCK_EX|LOCK_NB)
209 ) or die "Can't open $spooldir/domains/" . $domain . "/.qmail-default: $!";
211 ( open(VPASSWD,">$spooldir/domains/" . $domain . "/vpasswd")
212 and flock(VPASSWD,LOCK_EX|LOCK_NB)
213 ) or die "Can't open $spooldir/domains/" . $domain . "/vpasswd: $!";
217 if ($svc_domain->getfield('catchall')) {
218 $svc_acct = qsearchs('svc_acct', {'svcnum' => $svc_domain->catchall});
219 die "Cannot find catchall account for domain $domain\n" unless $svc_acct;
221 my $username = $svc_acct->username;
222 push @sendmail, "\@$domain\t$username\n";
223 print VIRTUALDOMAINS "$domain:$username-$domain\n",
224 ".$domain:$username-$domain\n",
228 # FORMAT OF THE .QMAIL-DEFAULT FILE HERE
229 print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" " . $svc_acct->email . "\n"
230 if $vpopmailmachines[0];
234 # FORMAT OF THE .QMAIL-DEFAULT FILE HERE
235 print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" bounce-no-mailbox\n"
236 if $vpopmailmachines[0];
239 print VPOPVIRTUALDOMAINS "$domain:$domain\n";
241 foreach $svc_acct (qsearch('svc_acct', {'domsvc' => $svc_domain->svcnum})) {
242 my($password)=$svc_acct->getfield('_password');
243 my($cpassword,$rpassword);
244 #if ( ( length($password) <= 8 )
245 if ( ( length($password) <= 12 )
246 && ( $password ne '*' )
247 && ( $password ne '!!' )
248 && ( $password ne '' )
250 $cpassword=crypt($password,
251 $saltset[int(rand(64))].$saltset[int(rand(64))]
253 $rpassword=$password;
255 $cpassword=$password;
261 if ($mydomain && ($mydomain eq $svc_domain->domain)) {
262 $username=$svc_acct->username;
263 } elsif ($userpolicy =~ /^prepend domsvc$/) {
264 $username=$svc_acct->domsvc . $svc_acct->username;
265 } elsif ($userpolicy =~ /^append domsvc$/) {
266 $username=$svc_acct->username . $svc_acct->domsvc;
267 } elsif ($userpolicy =~ /^append domain$/) {
268 $username=$svc_acct->username . $svc_domain->domain;
269 } elsif ($userpolicy =~ /^append domain$/) {
270 $username=$svc_acct->username . $svc_domain->domain;
271 } elsif ($userpolicy =~ /^append \@domain$/) {
272 $username=$svc_acct->username . '@'. $svc_domain->domain;
274 die "Unknown policy in username_policy\n";
277 if ($svc_acct->dir ne '/dev/null' || $svc_acct->slipip ne '') {
278 if ($usernames{$username}++) {
279 die "Duplicate username detected: $username\n";
283 if ( $svc_acct->uid =~ /^(\d+)$/ ) {
285 die "Non-root user ". $svc_acct->username. " has 0 UID!"
286 if $svc_acct->uid == 0 && $svc_acct->username ne 'root';
288 if ( $svc_acct->dir ne "/dev/null") {
291 # FORMAT OF FreeBSD MASTER PASSWD FILE HERE
292 print MASTER join(":",
293 $username, # User name
294 $cpassword, # Encrypted password
295 $svc_acct->uid, # User ID
296 $svc_acct->gid, # Group ID
298 "0", # Password Change Time
299 "0", # Password Expiration Time
300 $svc_acct->finger, # Users name
301 $svc_acct->dir, # Users home directory
302 $svc_acct->shell, # shell
307 # FORMAT OF THE PASSWD FILE HERE
308 print PASSWD join(":",
310 'x', # "##". $username,
319 # FORMAT OF THE SHADOW FILE HERE
320 print SHADOW join(":",
335 # FORMAT OF THE VPASSWD FILE HERE
336 print VPASSWD join(":",
342 "$vpopdir/domains/" . $svc_domain->domain ."/" . $svc_acct->username,
347 if ( $svc_acct->slipip ne '' ) {
350 # FORMAT OF THE ACP_* FILES HERE
351 print ACP_PASSWD join(":",
361 my($ip)=$svc_acct->slipip;
363 unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) {
364 print ACP_DIALUP $username, "\t*\t", $svc_acct->slipip, "\n";
367 my %radreply = $svc_acct->radius_reply;
368 my %radcheck = $svc_acct->radius_check;
370 my $radcheck = join ", ", map { qq($_ = "$radcheck{$_}") } keys %radcheck;
371 $radcheck .= ", " if $radcheck;
374 # FORMAT OF THE USERS FILE HERE
377 qq(\t${textradiusprepend}),
379 # qq(Password = "$rpassword"\n\t),
380 join ",\n\t", map { qq($_ = "$radreply{$_}") } keys %radreply;
382 #if ( $ip && $ip ne '0e0' ) {
383 # #print USERS qq(,\n\tFramed-Address = "$ip"\n\n);
384 # print USERS qq(,\n\tFramed-IP-Address = "$ip"\n\n);
386 print USERS qq(\n\n);
392 # vpopmail directory structure creation
394 (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username, 0700)
395 or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . ": $!";
396 (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir", 0700)
397 or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir: $!";
398 (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/cur", 0700)
399 or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/cur: $!";
400 (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/new", 0700)
401 or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/new: $!";
402 (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/tmp", 0700)
403 or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/tmp: $!";
405 ( open(DOTQMAIL,">$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail")
406 and flock(DOTQMAIL,LOCK_EX|LOCK_NB)
407 ) or die "Can't open $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail: $!";
410 foreach $svc_forward (qsearch('svc_forward', {'srcsvc' => $svc_acct->svcnum})) {
412 if ($svc_forward->dstsvc) {
413 my $dst_acct = qsearchs('svc_acct', {'svcnum' => $svc_forward->dstsvc});
414 my $dst_domain = qsearchs('svc_domain', {'svcnum' => $dst_acct->domsvc});
415 $destination = $dst_acct->username . '@' . $dst_domain->domain;
417 if ($dst_domain->domain eq $mydomain) {
418 print VIRTUSERTABLE $svc_acct->username . "@" . $svc_domain->domain .
419 "\t" . $dst_acct->username . "\n";
420 print RECIPIENTMAP $svc_acct->username . "@" . $svc_domain->domain .
424 $destination = $svc_forward->dst;
428 # FORMAT OF .QMAIL FILES HERE
429 print DOTQMAIL "$destination\n";
432 flock(DOTQMAIL,LOCK_UN);
437 flock(VPASSWD,LOCK_UN);
438 flock(QMAILDEFAULT,LOCK_UN);
445 # FORMAT OF THE ASSIGN/USERS FILE FINAL LINE HERE
448 print VIRTUSERTABLE @sendmail;
450 flock(MASTER,LOCK_UN);
451 flock(PASSWD,LOCK_UN);
452 flock(SHADOW,LOCK_UN);
453 flock(ACP_DIALUP,LOCK_UN);
454 flock(ACP_PASSWD,LOCK_UN);
455 flock(USERS,LOCK_UN);
456 flock(ASSIGN,LOCK_UN);
457 flock(SENDMAIL_CW,LOCK_UN);
458 flock(VIRTUSERTABLE,LOCK_UN);
459 flock(RCPTHOSTS,LOCK_UN);
460 flock(VPOPRCPTHOSTS,LOCK_UN);
461 flock(RECIPIENTMAP,LOCK_UN);
462 flock(VPOPVIRTUALDOMAINS,LOCK_UN);
476 close VPOPVIRTUALDOMAINS;
483 foreach $ashellmachine (@shellmachines) {
484 my $scp = new Net::SCP;
485 $scp->scp("$spooldir/passwd","root\@$ashellmachine:/etc/passwd.new")
486 or die "scp error: ". $scp->{errstr};
487 $scp->scp("$spooldir/shadow","root\@$ashellmachine:/etc/shadow.new")
488 or die "scp error: ". $scp->{errstr};
489 ssh("root\@$ashellmachine",
491 "mv /etc/passwd.new /etc/passwd; ".
492 "mv /etc/shadow.new /etc/shadow; ".
495 == 0 or die "ssh error: $!";
498 my($bsdshellmachine);
499 foreach $bsdshellmachine (@bsdshellmachines) {
500 my $scp = new Net::SCP;
501 $scp->scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new")
502 or die "scp error: ". $scp->{errstr};
503 $scp->scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new")
504 or die "scp error: ". $scp->{errstr};
505 ssh("root\@$bsdshellmachine",
507 "mv /etc/passwd.new /etc/passwd; ".
508 #"mv /etc/master.passwd.new /etc/master.passwd; ".
509 "pwd_mkdb /etc/master.passwd.new; ".
512 == 0 or die "ssh error: $!";
516 foreach $nismachine (@nismachines) {
517 my $scp = new Net::SCP;
518 $scp->scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd")
519 or die "scp error: ". $scp->{errstr};
520 $scp->scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow")
521 or die "scp error: ". $scp->{errstr};
522 ssh("root\@$nismachine",
524 "cd /var/yp; make; ".
527 == 0 or die "ssh error: $!";
531 foreach $erpcdmachine (@erpcdmachines) {
532 my $scp = new Net::SCP;
533 $scp->scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd")
534 or die "scp error: ". $scp->{errstr};
535 $scp->scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup")
536 or die "scp error: ". $scp->{errstr};
537 ssh("root\@$erpcdmachine",
539 "kill -USR1 \`cat /usr/annex/erpcd.pid\'".
542 == 0 or die "ssh error: $!";
546 foreach $radiusmachine (@radiusmachines) {
547 my $scp = new Net::SCP;
548 $scp->scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users")
549 or die "scp error: ". $scp->{errstr};
550 ssh("root\@$radiusmachine",
555 == 0 or die "ssh error: $!";
558 #my @args = ("/bin/tar", "c", "--force-local", "-C", "$spooldir", "-f", "$spooldir/vpoptarball", "domains");
560 #system {$args[0]} @args;
562 my($vpopmailmachine);
563 foreach $vpopmailmachine (@vpopmailmachines) {
564 my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachine);
565 my $scp = new Net::SCP;
566 # $scp->scp("$spooldir/vpoptarball","root\@$machine:vpoptarball")
567 # or die "scp error: ". $scp->{errstr};
568 # ssh("root\@$machine",
570 # "rm -rf domains; ".
571 # "tar xf vpoptarball; ".
572 # "chown -R $vpopuid:$vpopgid domains; ".
573 # "tar cf vpoptarball domains; ".
575 # "tar xf ~/vpoptarball; ".
578 # == 0 or die "ssh error: $!";
581 my @args = ("$rsync", "-rlpt", "-e", "$ssh", "domains/", "vpopmail\@$machine:$vpopdir/domains/");
583 system {$args[0]} @args;
585 $scp->scp("$spooldir/assign","root\@$machine:/var/qmail/users/assign")
586 or die "scp error: ". $scp->{errstr};
587 $scp->scp("$spooldir/vpopvirtualdomains","root\@$machine:/var/qmail/control/virtualdomains")
588 or die "scp error: ". $scp->{errstr};
589 $scp->scp("$spooldir/vpoprcpthosts","root\@$machine:/var/qmail/control/rcpthosts")
590 or die "scp error: ". $scp->{errstr};
592 ssh("root\@$machine",
597 == 0 or die "ssh error: $!";
602 my($sendmailmachine);
603 foreach $sendmailmachine (@sendmailmachines) {
604 my $scp = new Net::SCP;
605 $scp->scp("$spooldir/sendmail.cw","root\@$sendmailmachine:$sendmailconfigpath/sendmail.cw.new")
606 or die "scp error: ". $scp->{errstr};
607 $scp->scp("$spooldir/virtusertable","root\@$sendmailmachine:$sendmailconfigpath/virtusertable.new")
608 or die "scp error: ". $scp->{errstr};
609 ssh("root\@$sendmailmachine",
611 "mv $sendmailconfigpath/sendmail.cw.new $sendmailconfigpath/sendmail.cw; ".
612 "mv $sendmailconfigpath/virtusertable.new $sendmailconfigpath/virtusertable; ".
616 == 0 or die "ssh error: $!";
620 foreach $qmailmachine (@qmailmachines) {
621 my $scp = new Net::SCP;
622 $scp->scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap")
623 or die "scp error: ". $scp->{errstr};
624 $scp->scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains")
625 or die "scp error: ". $scp->{errstr};
626 $scp->scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts")
627 or die "scp error: ". $scp->{errstr};
628 #ssh("root\@$qmailmachine","/etc/init.d/qmail restart")
629 # == 0 or die "ssh error: $!";
633 flock(EXPORT,LOCK_UN);
639 die "Usage:\n\n svc_acct.export user\n";