Initial revision
[freeside.git] / bin / svc_acct.export
1 #!/usr/bin/perl -Tw
2 #
3 # Create and export password files: passwd, passwd.adjunct, shadow,
4 # acp_passwd, acp_userinfo, acp_dialup, users
5 #
6 # ivan@voicenet.com late august/september 96
7 # (the password encryption bits were from melody)
8 #
9 # use a temporary copy of svc_acct to minimize lock time on the real file,
10 # and skip blank entries.
11 #
12 # ivan@voicenet.com 96-Oct-6
13 #
14 # change users / acp_dialup file formats
15 # ivan@voicenet.com 97-jan-28-31
16 #
17 # change priority (after copies) to 19, not 10
18 # ivan@voicenet.com 97-feb-5
19 #
20 # added exit if stuff is already locked 97-apr-15
21 #
22 # rewrite ivan@sisd.com 98-mar-9
23 #
24 # Changed 'password' to '_password' because Pg6.3 reserves this word
25 # Added code to create a FreeBSD style master.passwd file
26 #   bmccane@maxbaud.net 98-Apr-3
27 #
28 # don't export non-root 0 UID's, even if they get put in the database
29 # ivan@sisd.com 98-jul-14
30 #
31 # Uses Idle_Timeout, Port_Limit, Framed_Netmask and Framed_Route if they
32 # exist; need some way to support arbitrary radius fields.  also 
33 # /var/spool/freeside/conf/ ivan@sisd.com 98-jul-26, aug-9
34 #
35 # OOPS!  added arbitrary radius fields (pry 98-aug-16) but forgot to say so.
36 # ivan@sisd.com 98-sep-18
37
38 use strict;
39 use Fcntl qw(:flock);
40 use FS::SSH qw(scp ssh);
41 use FS::UID qw(adminsuidsetup);
42 use FS::Record qw(qsearch fields);
43
44 my($fshellmachines)="/var/spool/freeside/conf/shellmachines";
45 my(@shellmachines);
46 if ( -e $fshellmachines ) {
47   open(SHELLMACHINES,$fshellmachines);
48   @shellmachines=map {
49     /^(.*)$/ or die "Illegal line in conf/shellmachines"; #we trust the file
50     $1;
51   } grep $_ !~ /^(#|$)/, <SHELLMACHINES>;
52   close SHELLMACHINES;
53 }
54
55 my($fbsdshellmachines)="/var/spool/freeside/conf/bsdshellmachines";
56 my(@bsdshellmachines);
57 if ( -e $fbsdshellmachines ) {
58   open(BSDSHELLMACHINES,$fbsdshellmachines);
59   @bsdshellmachines=map {
60     /^(.*)$/ or die "Illegal line in conf/bsdshellmachines"; #we trust the file
61     $1;
62   } grep $_ !~ /^(#|$)/, <BSDSHELLMACHINES>;
63   close BSDSHELLMACHINES;
64 }
65
66 my($fnismachines)="/var/spool/freeside/conf/nismachines";
67 my(@nismachines);
68 if ( -e $fnismachines ) {
69   open(NISMACHINES,$fnismachines);
70   @nismachines=map {
71     /^(.*)$/ or die "Illegal line in conf/nismachines"; #we trust the file
72     $1;
73   } grep $_ !~ /^(#|$)/, <NISMACHINES>;
74   close NISMACHINES;
75 }
76
77 my($ferpcdmachines)="/var/spool/freeside/conf/erpcdmachines";
78 my(@erpcdmachines);
79 if ( -e $ferpcdmachines ) {
80   open(ERPCDMACHINES,$ferpcdmachines);
81   @erpcdmachines=map {
82     /^(.*)$/ or die "Illegal line in conf/erpcdmachines"; #we trust the file
83     $1;
84   } grep $_ !~ /^(#|$)/, <ERPCDMACHINES>;
85   close ERPCDMACHINES;
86 }
87
88 my($fradiusmachines)="/var/spool/freeside/conf/radiusmachines";
89 my(@radiusmachines);
90 if ( -e $fradiusmachines ) {
91   open(RADIUSMACHINES,$fradiusmachines);
92   @radiusmachines=map {
93     /^(.*)$/ or die "Illegal line in conf/radiusmachines"; #we trust the file
94     $1;
95   } grep $_ !~ /^(#|$)/, <RADIUSMACHINES>;
96   close RADIUSMACHINES;
97 }
98
99 my($spooldir)="/var/spool/freeside/export";
100 my($spoollock)="/var/spool/freeside/svc_acct.export.lock";
101
102 adminsuidsetup;
103
104 my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
105 srand(time|$$);
106
107 open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!";
108 select(EXPORT); $|=1; select(STDOUT);
109 unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) {
110   seek(EXPORT,0,0);
111   my($pid)=<EXPORT>;
112   chop($pid);
113   #no reason to start loct of blocking processes
114   die "Is another export process running under pid $pid?\n";
115 }
116 seek(EXPORT,0,0);
117 print EXPORT $$,"\n";
118
119 my(@svc_acct)=qsearch('svc_acct',{});
120
121 ( open(MASTER,">$spooldir/master.passwd")
122   and flock(MASTER,LOCK_EX|LOCK_NB)
123 ) or die "Can't open $spooldir/master.passwd: $!";
124 ( open(PASSWD,">$spooldir/passwd")
125   and flock(PASSWD,LOCK_EX|LOCK_NB)  
126 ) or die "Can't open $spooldir/passwd: $!";
127 ( open(SHADOW,">$spooldir/shadow")
128   and flock(SHADOW,LOCK_EX|LOCK_NB) 
129 ) or die "Can't open $spooldir/shadow: $!";
130 ( open(ACP_PASSWD,">$spooldir/acp_passwd") 
131   and flock (ACP_PASSWD,LOCK_EX|LOCK_NB)
132 ) or die "Can't open $spooldir/acp_passwd: $!";
133 ( open (ACP_DIALUP,">$spooldir/acp_dialup")
134   and flock(ACP_DIALUP,LOCK_EX|LOCK_NB)
135 ) or die "Can't open $spooldir/acp_dialup: $!";
136 ( open (USERS,">$spooldir/users")
137   and flock(USERS,LOCK_EX|LOCK_NB)
138 ) or die "Can't open $spooldir/users: $!";
139
140 chmod 0644, "$spooldir/passwd",
141             "$spooldir/acp_dialup",
142 ;
143 chmod 0600, "$spooldir/master.passwd",
144             "$spooldir/acp_passwd",
145             "$spooldir/shadow",
146             "$spooldir/users",
147 ;
148
149 setpriority(0,0,10);
150
151 my($svc_acct);
152 foreach $svc_acct (@svc_acct) {
153
154   my($password)=$svc_acct->getfield('_password');
155   my($cpassword,$rpassword);
156   if ( ( length($password) <= 8 )
157        && ( $password ne '*' )
158        && ( $password ne '' )
159      ) {
160     $cpassword=crypt($password,
161                      $saltset[int(rand(64))].$saltset[int(rand(64))]
162     );
163     $rpassword=$password;
164   } else {
165     $cpassword=$password;
166     $rpassword='UNIX';
167   }
168
169   if ( $svc_acct->uid  =~ /^(\d+)$/ ) {
170
171     die "Non-root user ". $svc_acct->username. " has 0 UID!"
172       if $svc_acct->uid == 0 && $svc_acct->username ne 'root';
173
174     ###
175     # FORMAT OF FreeBSD MASTER PASSWD FILE HERE
176     print MASTER join(":",
177       $svc_acct->username,              # User name
178       $cpassword,                       # Encrypted password
179       $svc_acct->uid,                   # User ID
180       $svc_acct->gid,                   # Group ID
181       "",                               # Login Class
182       "0",                              # Password Change Time
183       "0",                              # Password Expiration Time
184       $svc_acct->finger,                # Users name
185       $svc_acct->dir,                   # Users home directory
186       $svc_acct->shell,                 # shell
187     ), "\n" ;
188
189     ###
190     # FORMAT OF THE PASSWD FILE HERE
191     print PASSWD join(":",
192       $svc_acct->username,
193       'x', # "##". $svc_acct->$username,
194       $svc_acct->uid,
195       $svc_acct->gid,
196       $svc_acct->finger,
197       $svc_acct->dir,
198       $svc_acct->shell,
199     ), "\n";
200
201     ###
202     # FORMAT OF THE SHADOW FILE HERE
203     print SHADOW join(":",
204       $svc_acct->username,
205       $cpassword,
206       '',
207       '',
208       '',
209       '',
210       '',
211       '',
212       '',
213     ), "\n";
214
215   }
216
217   if ( $svc_acct->slipip ne '' ) {
218
219     ###
220     # FORMAT OF THE ACP_* FILES HERE
221     print ACP_PASSWD join(":",
222       $svc_acct->username,
223       $cpassword,
224       "0",
225       "0",
226       "",
227       "",
228       "",
229     ), "\n";
230
231     my($ip)=$svc_acct->slipip;
232
233     unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) {
234       print ACP_DIALUP $svc_acct->username, "\t*\t", $svc_acct->slipip, "\n";
235     }
236
237     ###
238     # FORMAT OF THE USERS FILE HERE
239     print USERS
240       $svc_acct->username, qq(\tPassword = "$rpassword"\n\t),
241
242       join ",\n\t",
243         map  {
244           /^(radius_(.*))$/;
245           my($field,$attrib)=($1,$2);
246           $attrib =~ s/_/\-/g;
247           "$attrib = \"". $svc_acct->getfield($field). "\"";
248         } grep /^radius_/ && $svc_acct->getfield($_), fields('svc_acct') 
249     ;
250     if ( $ip && $ip ne '0e0' ) {
251       print USERS qq(,\n\tFramed-Address = "$ip"\n\n);
252     } else {
253       print USERS qq(\n\n);
254     }
255
256   }
257
258 }
259
260 flock(MASTER,LOCK_UN);
261 flock(PASSWD,LOCK_UN);
262 flock(SHADOW,LOCK_UN);
263 flock(ACP_DIALUP,LOCK_UN);
264 flock(ACP_PASSWD,LOCK_UN);
265 flock(USERS,LOCK_UN);
266
267 close MASTER;
268 close PASSWD;
269 close SHADOW;
270 close ACP_DIALUP;
271 close ACP_PASSWD;
272 close USERS;
273
274 ###
275 # export stuff
276 #
277
278 my($shellmachine);
279 foreach $shellmachine (@shellmachines) {
280   scp("$spooldir/passwd","root\@$shellmachine:/etc/passwd.new")
281     == 0 or die "scp error: $!";
282   scp("$spooldir/shadow","root\@$shellmachine:/etc/shadow.new")
283     == 0 or die "scp error: $!";
284   ssh("root\@$shellmachine",
285     "( ".
286       "mv /etc/passwd.new /etc/passwd; ".
287       "mv /etc/shadow.new /etc/shadow; ".
288     " )"
289   )
290     == 0 or die "ssh error: $!";
291 }
292
293 my($bsdshellmachine);
294 foreach $bsdshellmachine (@bsdshellmachines) {
295   scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new")
296     == 0 or die "scp error: $!";
297   scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new")
298     == 0 or die "scp error: $!";
299   ssh("root\@$bsdshellmachine",
300     "( ".
301       "mv /etc/passwd.new /etc/passwd; ".
302       "mv /etc/master.passwd.new /etc/master.passwd; ".
303     " )"
304   )
305     == 0 or die "ssh error: $!";
306 }
307
308 my($nismachine);
309 foreach $nismachine (@nismachines) {
310   scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd")
311     == 0 or die "scp error: $!";
312   scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow")
313     == 0 or die "scp error: $!";
314   ssh("root\@$nismachine",
315     "( ".
316       "cd /var/yp; make; ".
317     " )"
318   )
319     == 0 or die "ssh error: $!";
320 }
321
322 my($erpcdmachine);
323 foreach $erpcdmachine (@erpcdmachines) {
324   scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd")
325     == 0 or die "scp error: $!";
326   scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup")
327     == 0 or die "scp error: $!";
328   ssh("root\@$erpcdmachine",
329     "( ".
330       "kill -USR1 \`cat /usr/annex/erpcd.pid\'".
331     " )"
332   )
333     == 0 or die "ssh error: $!";
334 }
335
336 my($radiusmachine);
337 foreach $radiusmachine (@radiusmachines) {
338   scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users")
339     == 0 or die "scp error: $!";
340   ssh("root\@$erpcdmachine",
341     "( ".
342       "builddbm".
343     " )"
344   )
345     == 0 or die "ssh error: $!";
346 }
347
348 unlink $spoollock;
349 flock(EXPORT,LOCK_UN);
350 close EXPORT;
351