/[soft]/drakx/trunk/perl-install/authentication.pm
ViewVC logotype

Contents of /drakx/trunk/perl-install/authentication.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3714 - (show annotations) (download)
Fri Mar 23 20:50:51 2012 UTC (7 years, 1 month ago) by tv
File size: 33513 byte(s)
clean dead code that was commented years ago...
1 package authentication; # $Id: authentication.pm 269894 2010-06-05 20:50:23Z tv $
2
3 use common;
4
5 my $authentication;
6
7 sub kinds {
8 my $no_para = @_ == 0;
9 my ($do_pkgs, $_meta_class) = @_;
10 my $allow_SmartCard = $no_para || $do_pkgs->is_available('castella-pam');
11 (
12 'LDAP',
13 'KRB5',
14 'winbind',
15 'NIS',
16 if_($allow_SmartCard, 'SmartCard'),
17 'local',
18 );
19 }
20
21 sub kind2name {
22 my ($kind) = @_;
23 # Keep the following strings in sync with kind2description ones!!!
24 ${{ local => N("Local file"),
25 LDAP => N("LDAP"),
26 NIS => N("NIS"),
27 SmartCard => N("Smart Card"),
28 winbind => N("Windows Domain"),
29 KRB5 => N("Kerberos 5") }}{$kind};
30 }
31
32 my %kind2pam_kind = (
33 local => [],
34 SmartCard => ['castella'],
35 LDAP => ['ldap'],
36 NIS => [],
37 KRB5 => ['krb5'],
38 winbind => ['winbind'],
39 );
40
41 my %kind2nsswitch = (
42 local => [],
43 SmartCard => [],
44 LDAP => ['ldap'],
45 NIS => ['nis'],
46 KRB5 => ['ldap'],
47 winbind => ['winbind'],
48 );
49
50 my $lib = (arch() =~ /x86_64/ ? 'lib64' : 'lib');
51
52 my %kind2packages = (
53 local => [],
54 SmartCard => [ 'castella-pam' ],
55 LDAP => [ 'openldap-clients', 'nss_ldap', 'pam_ldap', 'autofs', 'nss_updatedb' ],
56 KRB5 => [ 'nss_ldap', 'pam_krb5', "${lib}sasl2-plug-gssapi", 'nss_updatedb' ],
57 NIS => [ 'ypbind', 'autofs' ],
58 winbind => [ 'samba-winbind', 'nss_ldap', 'pam_krb5', "${lib}sasl2-plug-gssapi" ],
59 );
60
61
62 sub kind2description_raw {
63 my (@kinds) = @_;
64 my %kind2description = (
65 local => [ N("Local file:"), N("Use local for all authentication and information user tell in local file"), ],
66 LDAP => [ N("LDAP:"), N("Tells your computer to use LDAP for some or all authentication. LDAP consolidates certain types of information within your organization."), ],
67 NIS => [ N("NIS:"), N("Allows you to run a group of computers in the same Network Information Service domain with a common password and group file."), ],
68 winbind => [ N("Windows Domain:"), N("Winbind allows the system to retrieve information and authenticate users in a Windows domain."), ],
69 KRB5 => [ N("Kerberos 5 :"), N("With Kerberos and LDAP for authentication in Active Directory Server "), ],
70 );
71 join('', map { $_ ? qq($_->[0]\n$_->[1]) : '' } map { $kind2description{$_} } @kinds);
72 }
73
74 sub kind2description {
75 my (@kinds) = @_;
76 join('', map { $_ ? qq($_\n\n) : '' } map { kind2description_raw($_) } @kinds);
77 }
78
79 sub to_kind {
80 my ($authentication) = @_;
81 (find { exists $authentication->{$_} } kinds()) || 'local';
82 }
83
84 sub domain_to_ldap_domain {
85 my ($domain) = @_;
86 join(',', map { "dc=$_" } split /\./, $domain);
87 }
88
89 sub ask_parameters {
90 my ($in, $net, $authentication, $kind) = @_;
91
92 #- keep only this authentication kind
93 foreach (kinds()) {
94 delete $authentication->{$_} if $_ ne $kind;
95 }
96 # do not enable ccreds unless required
97 undef $authentication->{ccreds};
98
99 if ($kind eq 'LDAP') {
100 $authentication->{LDAPDOMAIN} ||= domain_to_ldap_domain($net->{resolv}{DOMAINNAME});
101 $authentication->{ccreds} = 1;
102
103 # this package must be installed for 'Fetch DN' button to actually work
104 $in->do_pkgs->ensure_are_installed([ 'openldap-clients' ], 1) or return;
105
106 $in->ask_from('', N(" "),
107 [ { label => N("Welcome to the Authentication Wizard"), title => 1 },
108 {},
109 { label => N("You have selected LDAP authentication. Please review the configuration options below "), },
110 {},
111 { label => N("LDAP Server"), val => \$authentication->{LDAP_server} },
112 { label => N("Base dn"), val => \$authentication->{LDAPDOMAIN} },
113 { val => N("Fetch base Dn "), type => 'button' , clicked_may_quit => sub { $authentication->{LDAPDOMAIN} = fetch_dn($authentication->{LDAP_server}); 0 } },
114 {},
115 { text => N("Use encrypt connection with TLS "), val => \$authentication->{cafile}, type => 'bool' },
116 { val => N("Download CA Certificate "), type => 'button' , disabled => sub { !$authentication->{cafile} }, clicked_may_quit => sub { $authentication->{file} = add_cafile(); 0 } },
117
118 { text => N("Use Disconnect mode "), val => \$authentication->{ccreds}, type => 'bool' },
119 { text => N("Use anonymous BIND "), val => \$authentication->{anonymous}, type => 'bool' , advanced => 1 },
120 { text => N(" "), advanced => 1 },
121 { label => N("Bind DN "), val => \$authentication->{LDAP_binddn}, disabled => sub { !$authentication->{anonymous} }, advanced => 1 },
122 { label => N("Bind Password "), val => \$authentication->{LDAP_bindpwd}, disabled => sub { !$authentication->{anonymous} }, advanced => 1 },
123 { text => N(" "), advanced => 1 },
124 { text => N("Advanced path for group "), val => \$authentication->{nssgrp}, type => 'bool' , advanced => 1 },
125 { text => N(" "), advanced => 1 },
126 { label => N("Password base"), val => \$authentication->{nss_pwd}, disabled => sub { !$authentication->{nssgrp} }, advanced => 1 },
127 { label => N("Group base"), val => \$authentication->{nss_grp}, disabled => sub { !$authentication->{nssgrp} }, advanced => 1 },
128 { label => N("Shadow base"), val => \$authentication->{nss_shadow}, disabled => sub { !$authentication->{nssgrp} }, advanced => 1 },
129 { text => N(" "), advanced => 1 },
130 ]) or return;
131 } elsif ($kind eq 'KRB5') {
132
133 $authentication->{AD_domain} ||= $net->{resolv}{DOMAINNAME};
134 $in->do_pkgs->ensure_are_installed([ 'perl-Net-DNS' ], 1) or return;
135 my @srvs = query_srv_names($authentication->{AD_domain}); #FIXME: update this list if the REALM has changed
136 $authentication->{AD_server} ||= $srvs[0] if @srvs;
137 my $AD_user = $authentication->{AD_user} =~ /(.*)\@\Q$authentication->{AD_domain}\E$/ ? $1 : $authentication->{AD_user};
138 $authentication->{ccreds} = 1;
139
140 $in->ask_from('', N(" "),
141 [ { label => N("Welcome to the Authentication Wizard"), title => 1 },
142 {},
143 { label => N("You have selected Kerberos 5 authentication. Please review the configuration options below "), },
144 {},
145 { label => N("Realm "), val => \$authentication->{AD_domain} },
146 {},
147 { label => N("KDCs Servers"), title => 1, val => \$authentication->{AD_server} , list => \@srvs , not_edit => 0, title => 1 },
148 {},
149 { text => N("Use DNS to locate KDC for the realm"), val => \$authentication->{KRB_host_lookup}, type => 'bool' },
150 { text => N("Use DNS to locate realms"), val => \$authentication->{KRB_dns_lookup}, type => 'bool' },
151 { text => N("Use Disconnect mode "), val => \$authentication->{ccreds}, type => 'bool' },
152 ]) or return;
153
154 my %level = (
155 1 => N("Use local file for users information"),
156 2 => N("Use LDAP for users information"),
157 );
158
159 $in->ask_from('', N(" "),
160 [ { label => N(" "), title => 1 },
161 {},
162 { label => N("You have selected Kerberos 5 for authentication, now you must choose the type of users information "), },
163 {},
164 { label => "" , val => \$authentication->{nsskrb}, type => 'list', list => [ keys %level ], format => sub { $level{$_[0]} } },
165 {},
166 { label => N("LDAP Server"), val => \$authentication->{LDAP_server}, disabled => sub { $authentication->{nsskrb} eq "1" } },
167 { label => N("Base dn"), val => \$authentication->{LDAPDOMAIN} , disabled => sub { $authentication->{nsskrb} eq "1" } },
168 { val => N("Fecth base Dn "), type => 'button' , clicked_may_quit => sub { $authentication->{LDAPDOMAIN} = fetch_dn($authentication->{LDAP_server}); 0 }, disabled => sub { $authentication->{nsskrb} eq "1" } },
169 {},
170 { text => N("Use encrypt connection with TLS "), val => \$authentication->{cafile}, type => 'bool',, disabled => sub { $authentication->{nsskrb} eq "1" } },
171 { val => N("Download CA Certificate "), type => 'button' , disabled => sub { !$authentication->{cafile} }, clicked_may_quit => sub { $authentication->{file} = add_cafile(); 0 } },
172 { text => N("Use anonymous BIND "), val => \$authentication->{anonymous}, type => 'bool', disabled => sub { $authentication->{nsskrb} eq "1" } },
173 { label => N("Bind DN "), val => \$authentication->{LDAP_binddn}, disabled => sub { !$authentication->{anonymous} } },
174 { label => N("Bind Password "), val => \$authentication->{LDAP_bindpwd}, disabled => sub { !$authentication->{anonymous} } },
175 {},
176 ]) or return;
177
178 $authentication->{AD_user} = !$AD_user || $authentication->{sub_kind} eq 'anonymous' ? '' :
179 $AD_user =~ /@/ ? $AD_user : "$AD_user\@$authentication->{AD_domain}";
180 $authentication->{AD_password} = '' if !$authentication->{AD_user};
181
182
183 } elsif ($kind eq 'NIS') {
184 $authentication->{NIS_server} ||= 'broadcast';
185 $net->{network}{NISDOMAIN} ||= $net->{resolv}{DOMAINNAME};
186 $in->ask_from('', N(" "),
187 [ { label => N("Welcome to the Authentication Wizard"), title => 1 },
188 {},
189 { label => N("You have selected NIS authentication. Please review the configuration options below "), },
190 {},
191 { label => N("NIS Domain"), val => \$net->{network}{NISDOMAIN} },
192 { label => N("NIS Server"), val => \$authentication->{NIS_server}, list => ["broadcast"], not_edit => 0 },
193 {},
194 ]) or return;
195 } elsif ($kind eq 'winbind') {
196 #- maybe we should browse the network like diskdrake --smb and get the 'doze server names in a list
197 #- but networking is not setup yet necessarily
198 #
199 my @sec_domain = (
200 "Windows Active Directory Domain",
201 "Windows NT4 Domain",
202 );
203
204
205 $authentication->{DNS_domain} ||= $net->{resolv}{DOMAINNAME};
206 $authentication->{WINDOMAIN} ||= $net->{resolv}{DOMAINNAME};
207 $in->do_pkgs->ensure_are_installed([ 'samba-client' ], 1) or return;
208 my @domains=list_domains();
209
210 $in->ask_from('', N(" "),
211 [ { label => N("Welcome to the Authentication Wizard"), title => 1 },
212 {},
213 { label => N("You have selected Windows Domain authentication. Please review the configuration options below "), },
214 {},
215 { label => N("Windows Domain"), val => \$authentication->{WINDOMAIN}, list => \@domains, not_edit => 1 },
216 {},
217 { label => N("Domain Model "), val => \$authentication->{model}, list => \@sec_domain , not_edit => 1 },
218 {},
219 { label => N("Active Directory Realm "), val => \$authentication->{AD_domain} , disabled => sub { $authentication->{model} eq "Windows NT4 Domain" } },
220 { label => N("DNS Domain"), val => \$authentication->{DNS_domain} , disabled => sub { $authentication->{model} eq "Windows NT4 Domain" } },
221 { label => N("DC Server"), val => \$authentication->{AD_server} , disabled => sub { $authentication->{model} eq "Windows NT4 Domain" } },
222 {},
223 ]) or return;
224 }
225 $authentication->{$kind} ||= 1;
226 1;
227 }
228 sub ask_root_password_and_authentication {
229 my ($in, $net, $superuser, $authentication, $meta_class, $security) = @_;
230
231 my $kind = to_kind($authentication);
232 my @kinds = kinds($in->do_pkgs, $meta_class);
233
234 $in->ask_from_({
235 title => N("Authentication"),
236 messages => N("Set administrator (root) password"),
237 advanced_label => N("Authentication method"),
238 advanced_messages => kind2description(@kinds),
239 interactive_help_id => "setRootPassword",
240 cancel => ($security <= 2 ?
241 #-PO: keep this short or else the buttons will not fit in the window
242 N("No password") : ''),
243 focus_first => 1,
244 callbacks => {
245 complete => sub {
246 check_given_password($in, $superuser, 2 * $security) or return 1,0;
247 return 0;
248 } } }, [
249 { label => N("Password"), val => \$superuser->{password}, hidden => 1 },
250 { label => N("Password (again)"), val => \$superuser->{password2}, hidden => 1 },
251 { label => N("Authentication"), val => \$kind, type => 'list', list => \@kinds, format => \&kind2name, advanced => 1 },
252 ]) or delete $superuser->{password};
253
254 ask_parameters($in, $net, $authentication, $kind) or goto &ask_root_password_and_authentication;
255 }
256
257 sub check_given_password {
258 my ($in, $u, $min_length) = @_;
259 if ($u->{password} ne $u->{password2}) {
260 $in->ask_warn('', [ N("The passwords do not match"), N("Please try again") ]);
261 0;
262 } elsif (length $u->{password} < $min_length) {
263 $in->ask_warn('', N("This password is too short (it must be at least %d characters long)", $min_length));
264 0;
265 } else {
266 1;
267 }
268 }
269
270 sub get() {
271 my $system_auth = cat_("/etc/pam.d/system-auth");
272 my $authentication = {
273 blowfish => to_bool($system_auth =~ /\$2a\$/),
274 md5 => to_bool($system_auth =~ /md5/),
275 shadow => to_bool($system_auth =~ /shadow/),
276 };
277
278 my @pam_kinds = get_pam_authentication_kinds();
279 if (my $kind = find { intersection(\@pam_kinds, $kind2pam_kind{$_}) } keys %kind2pam_kind) {
280 $authentication->{$kind} = '';
281 } else {
282 #- we can't use pam to detect NIS
283 if (my $yp_conf = read_yp_conf()) {
284 $authentication->{NIS} = 1;
285 map_each { $authentication->{"NIS_$::a"} = $::b } %$yp_conf;
286 }
287 }
288 $authentication;
289 }
290
291 sub install_needed_packages {
292 my ($do_pkgs, $kind, $ccreds) = @_;
293 if (my $pkgs = $kind2packages{$kind}) {
294 # install ccreds if required
295 $ccreds and push(@$pkgs, 'pam_ccreds');
296 #- automatic during install
297 $do_pkgs->ensure_are_installed($pkgs, $::isInstall) or return;
298 } else {
299 log::l("ERROR: $kind not listed in kind2packages");
300 }
301 1;
302 }
303
304 sub set {
305 my ($in, $net, $authentication, $o_when_network_is_up) = @_;
306
307 install_needed_packages($in->do_pkgs, to_kind($authentication), $authentication->{ccreds}) or return;
308 set_raw($net, $authentication, $o_when_network_is_up);
309
310 require services;
311 services::set_status('network-auth', to_kind($authentication) ne 'local', 'dont_apply');
312 }
313
314 sub set_raw {
315 my ($net, $authentication, $o_when_network_is_up) = @_;
316
317 my $conf_file = "$::prefix/etc/sysconfig/drakauth";
318 my $when_network_is_up = $o_when_network_is_up || sub { my ($f) = @_; $f->() };
319
320 enable_shadow() if $authentication->{shadow};
321
322 my $kind = to_kind($authentication);
323
324 log::l("authentication::set $kind");
325
326 my $pam_modules = $kind2pam_kind{$kind} or log::l("kind2pam_kind does not know $kind");
327 $pam_modules ||= [];
328 sshd_config_UsePAM(@$pam_modules > 0);
329 set_pam_authentication($pam_modules, $authentication->{ccreds});
330
331 my $nsswitch = $kind2nsswitch{$kind} or log::l("kind2nsswitch does not know $kind");
332 $nsswitch ||= [];
333 set_nsswitch_priority($nsswitch, $authentication->{ccreds});
334
335 if ($kind eq 'local') {
336
337 output($conf_file, <<EOF);
338 auth=Local File
339 server=none
340 realm=none
341 EOF
342
343
344
345 } elsif ($kind eq 'SmartCard') {
346 } elsif ($kind eq 'LDAP') {
347
348 configure_nss_ldap($authentication);
349
350 output($conf_file, <<EOF);
351 auth=LDAP Directory
352 server=$authentication->{LDAP_server}
353 realm=$authentication->{LDAPDOMAIN}
354 EOF
355
356 if ($authentication->{ccreds}) {
357 run_program::rooted($::prefix, '/usr/sbin/nss_updatedb.cron'); # updates offline cache.
358 }
359
360 } elsif ($kind eq 'KRB5') {
361
362 configure_krb5_for_AD($authentication);
363 configure_nss_ldap($authentication);
364
365 output($conf_file, <<EOF);
366 auth=Kerberos 5
367 server=$authentication->{AD_server}
368 realm=$authentication->{AD_domain}
369 EOF
370
371 } elsif ($kind eq 'NIS') {
372 my $domain = $net->{network}{NISDOMAIN};
373 my $NIS_server = $authentication->{NIS_server};
374 $domain || $NIS_server ne "broadcast" or die N("Cannot use broadcast with no NIS domain");
375 my $t = $domain ?
376 ($NIS_server eq 'broadcast' ?
377 "domain $domain broadcast" :
378 "domain $domain server $NIS_server") :
379 "server $NIS_server";
380
381 substInFile {
382 if (/^#/) {
383 $_ = '' if /^#\Q[PREVIOUS]/;
384 } else {
385 $_ = "#[PREVIOUS] $_";
386 }
387 $_ .= "$t\n" if eof;
388 } "$::prefix/etc/yp.conf";
389
390 #- no need to modify system-auth for nis
391
392 $when_network_is_up->(sub {
393 run_program::rooted($::prefix, 'nisdomainname', $domain);
394 run_program::rooted($::prefix, 'service', 'ypbind', 'restart');
395 });
396
397 output($conf_file, <<EOF);
398 auth=$kind
399 server=$NIS_server
400 realm=$domain
401 EOF
402
403 } elsif ($kind eq 'winbind') {
404
405 my $domain = uc $authentication->{WINDOMAIN};
406 ($authentication->{winuser}, $authentication->{winpass}) = auth();
407
408 if ($authentication->{model} eq "Windows NT4 Domain") {
409
410 require fs::remote::smb;
411 fs::remote::smb::write_smb_conf($domain);
412 run_program::rooted($::prefix, "chkconfig", "--level", "35", "winbind", "on");
413 mkdir_p("$::prefix/home/$domain");
414 run_program::rooted($::prefix, 'service', 'smb', 'restart');
415 run_program::rooted($::prefix, 'service', 'winbind', 'restart');
416
417 #- defer running smbpassword until the network is up
418
419 $when_network_is_up->(sub {
420 run_program::raw({ root => $::prefix, sensitive_arguments => 1 },
421 #'net', 'join', $domain, '-U', $authentication->{winuser} . '%' . $authentication->{winpass});
422 'echo', '"', 'net', 'join', $domain, '-U', $authentication->{winuser} . '%' . $authentication->{winpass}, '"');
423 });
424
425 output($conf_file, <<EOF);
426 auth=Windows NT4 Domain
427 server= none
428 realm=$domain
429 EOF
430
431
432
433
434 } else {
435 # FIXME: the DC isn't named ads.domain... try to do reserve lookup?
436 $authentication->{AD_server} ||= 'ads.' . $authentication->{AD_domain};
437 my $domain = uc $authentication->{WINDOMAIN};
438 my $realm = $authentication->{AD_domain};
439 ($authentication->{winuser}, $authentication->{winpass}) = auth();
440 configure_krb5_for_AD($authentication);
441
442 require fs::remote::smb;
443 fs::remote::smb::write_smb_ads_conf($domain,$realm);
444 run_program::rooted($::prefix, "chkconfig", "--level", "35", "winbind", "on");
445 mkdir_p("$::prefix/home/$domain");
446 run_program::rooted($::prefix, 'net', 'time', 'set', '-S', $authentication->{AD_server});
447 run_program::rooted($::prefix, 'service', 'smb', 'restart');
448
449 $when_network_is_up->(sub {
450 run_program::raw({ root => $::prefix, sensitive_arguments => 1 },
451 'net', 'ads', 'join', '-U', $authentication->{winuser} . '%' . $authentication->{winpass});
452 run_program::rooted($::prefix, 'service', 'winbind', 'restart');
453 });
454
455 #FIXME: perhaps save the defaults values ?
456 output($conf_file, <<EOF);
457 auth=Windows Active Directory Domain
458 server= none
459 realm=$realm
460 EOF
461 } }
462 1;
463 }
464
465
466 sub pam_modules() {
467 'pam_ldap', 'pam_castella', 'pam_winbind', 'pam_krb5', 'pam_mkhomedir', 'pam_ccreds', 'pam_deny' , 'pam_permit';
468 }
469 sub pam_module_from_path {
470 $_[0] && $_[0] =~ m|(/lib/security/)?(pam_.*)\.so| && $2;
471 }
472 sub pam_module_to_path {
473 "$_[0].so";
474 }
475 sub pam_format_line {
476 my ($type, $control, $module, @para) = @_;
477 sprintf("%-11s %-13s %s\n", $type, $control, join(' ', pam_module_to_path($module), @para));
478 }
479
480 sub get_raw_pam_authentication() {
481 my %before_deny;
482 foreach (cat_("$::prefix/etc/pam.d/system-auth")) {
483 my ($type, $_control, $other) = /(\S+)\s+(\[.*?\]|\S+)\s+(.*)/;
484 my ($module, @para) = split(' ', $other);
485 if ($module = pam_module_from_path($module)) {
486 $before_deny{$type}{$module} = \@para if member($module, pam_modules());
487 }
488 }
489 \%before_deny;
490 }
491
492 sub get_pam_authentication_kinds() {
493 my $before_deny = get_raw_pam_authentication();
494 map { s/pam_//; $_ } keys %{$before_deny->{auth}};
495 }
496
497 sub sufficient {
498 my ($ccreds, $module, $type) = @_;
499
500 $ccreds && member($module, 'pam_tcb' , 'pam_winbind') ?
501 'sufficient' :
502 $ccreds && member($module, 'pam_ldap', 'pam_krb5') && $type eq 'account' ?
503 '[authinfo_unavail=ignore default=done]' :
504 $ccreds && member($module, 'pam_ldap', 'pam_krb5') && $type eq 'password' ?
505 'sufficient' :
506 $ccreds && member($module, 'pam_ldap', 'pam_krb5') ?
507 '[authinfo_unavail=ignore user_unknown=ignore success=1 default=2]' :
508 'sufficient';
509 }
510
511 sub pam_sufficient_line {
512 my ($ccreds, $type, $module, @para) = @_;
513 my $control = sufficient($ccreds, $module, $type);
514 if ($module eq 'pam_winbind') {
515 push @para, 'cached_login';
516 }
517 pam_format_line($type, $control, $module, @para);
518 }
519
520
521
522
523
524
525 sub set_pam_authentication {
526 my ($authentication_kinds, $o_ccreds) = @_;
527
528 my %special = (
529 auth => [ difference2($authentication_kinds,, [ 'mount' ]) ],
530 account => [ difference2($authentication_kinds, [ 'castella', 'mount', 'ccreds' ]) ],
531 password => [ intersection($authentication_kinds, [ 'ldap', 'krb5', 'ccreds' ]) ],
532 );
533 my %before_first = (
534 auth => member('mount', @$authentication_kinds) ? pam_format_line('auth', 'required', 'pam_mount') : '',
535 session =>
536 intersection($authentication_kinds, [ 'winbind', 'krb5', 'ldap' ])
537 ? pam_format_line('session', 'optional', 'pam_mkhomedir', 'skel=/etc/skel/', 'umask=0022') :
538 member('castella', @$authentication_kinds)
539 ? pam_format_line('session', 'optional', 'pam_castella') : '',
540 );
541 my %after_deny = (
542 session =>
543 member('krb5', @$authentication_kinds)
544 ? pam_format_line('session', 'optional', 'pam_krb5') :
545 member('mount', @$authentication_kinds)
546 ? pam_format_line('session', 'optional', 'pam_mount') : '',
547 );
548
549 substInFile {
550 my ($type, $control, $other) = /(\S+)\s+(\[.*?\]|\S+)\s+(.*)/;
551 my ($module, @para) = split(' ', $other);
552 if ($module = pam_module_from_path($module)) {
553 if (member($module, pam_modules())) {
554 #- first removing previous config
555 $_ = '';
556 }
557 if ($module eq 'pam_tcb' && $special{$type}) {
558 my @para_for_last =
559 member($type, 'auth', 'account') ? qw(use_first_pass) : @{[]};
560 @para = difference2(\@para, \@para_for_last);
561
562 my ($before_noask, $ask) = partition { $_ eq 'castella' } @{$special{$type}};
563
564 if (!@$ask) {
565 @para_for_last = grep { $_ ne 'use_first_pass' } @para_for_last;
566 }
567
568 my @l = ((map { [ "pam_$_" ] } @$before_noask),
569 [ 'pam_tcb', @para ],
570 (map { [ "pam_$_" ] } @$ask),
571 );
572 push @{$l[-1]}, @para_for_last;
573
574 $_ = join('', map { pam_sufficient_line($o_ccreds, $type, @$_) } @l);
575
576 if ($control eq 'required') {
577 #- ensure a pam_deny line is there. it will be added below
578 ($module, @para) = ('pam_deny');
579 }
580
581 if ($type eq 'auth' && $o_ccreds) {
582 $_ .= pam_format_line('auth', '[default=done]', 'pam_ccreds', 'action=validate use_first_pass');
583 $_ .= pam_format_line('auth', '[default=done]', 'pam_ccreds', 'action=store');
584 $_ .= pam_format_line('auth', '[default=bad]', 'pam_ccreds', 'action=update');
585 }
586 }
587
588
589 if (member($module, 'pam_deny', 'pam_permit')) {
590 $_ .= pam_format_line($type, $control,
591 $type eq 'account' && $o_ccreds ? 'pam_permit' : 'pam_deny');
592 }
593 if (my $s = delete $before_first{$type}) {
594 $_ = $s . $_;
595 }
596 if ($control eq 'required' && member($module, 'pam_deny', 'pam_permit', 'pam_tcb')) {
597 if (my $s = delete $after_deny{$type}) {
598 $_ .= $s;
599 }
600 }
601 }
602 } "$::prefix/etc/pam.d/system-auth";
603 }
604
605 sub set_nsswitch_priority {
606 my ($kinds, $connected) = @_;
607 my @known = qw(nis ldap winbind compat);
608 substInFile {
609 if (my ($database, $l) = /^(\s*(?:passwd|shadow|group|automount):\s*)(.*)/) {
610 my @l = difference2([ split(' ', $l) ], \@known);
611 $_ = $database . join(' ', uniq('files', @$kinds, @l)) . "\n";
612 }
613 if (/^\s*(?:passwd|group):/) {
614 my $option = '[NOTFOUND=return] db';
615 if ($connected) {
616 s/$/ $option/ if !/\Q$option/;
617 } else {
618 s/\s*\Q$option//;
619 }
620 }
621
622 } "$::prefix/etc/nsswitch.conf";
623 }
624
625 sub read_yp_conf() {
626 my $yp_conf = cat_("$::prefix/etc/yp.conf");
627
628 if ($yp_conf =~ /^domain\s+(\S+)\s+(\S+)\s*(.*)/m) {
629 { domain => $1, server => $2 eq 'broadcast' ? 'broadcast' : $3 };
630 } elsif ($yp_conf =~ /^server\s+(.*)/m) {
631 { server => $1 };
632 } else {
633 undef;
634 }
635 }
636
637 my $special_ldap_cmds = join('|', 'nss_map_attribute', 'nss_map_objectclass');
638 sub _after_read_ldap_line {
639 my ($s) = @_;
640 $s =~ s/\b($special_ldap_cmds)\s*/$1 . '_'/e;
641 $s;
642 }
643 sub _pre_write_ldap_line {
644 my ($s) = @_;
645 $s =~ s/\b($special_ldap_cmds)_/$1 . ' '/e;
646 $s;
647 }
648
649 sub read_ldap_conf() {
650 my %conf = map {
651 s/^\s*#.*//;
652 if_(_after_read_ldap_line($_) =~ /(\S+)\s+(.*)/, $1 => $2);
653 } cat_("$::prefix/etc/ldap.conf");
654 \%conf;
655 }
656
657 sub update_ldap_conf {
658 my (%conf) = @_;
659
660 substInFile {
661 my ($cmd) = _after_read_ldap_line($_) =~ /^\s*#?\s*(\w+)\s/;
662 if ($cmd && exists $conf{$cmd}) {
663 my $val = $conf{$cmd};
664 $conf{$cmd} = '';
665 $_ = $val ? _pre_write_ldap_line("$cmd $val\n") : /^\s*#/ ? $_ : "#$_";
666 }
667 if (eof) {
668 foreach my $cmd (keys %conf) {
669 my $val = $conf{$cmd} or next;
670 $_ .= _pre_write_ldap_line("$cmd $val\n");
671 }
672 }
673 } "$::prefix/etc/ldap.conf";
674 }
675
676 sub configure_krb5_for_AD {
677 my ($authentication) = @_;
678
679 my $uc_domain = uc $authentication->{AD_domain};
680 my $krb5_conf_file = "$::prefix/etc/krb5.conf";
681
682 krb5_conf_update($krb5_conf_file,
683 libdefaults => (
684 default_realm => $uc_domain,
685 dns_lookup_realm => $authentication->{KRB_dns_lookup} ? 'true' : 'false',
686 dns_lookup_kdc => $authentication->{KRB_host_lookup} ? 'true' : 'false',
687 default_tgs_enctypes => undef,
688 default_tkt_enctypes => undef,
689 permitted_enctypes => undef,
690 ));
691
692 my @sections = (
693 realms => <<EOF,
694 $uc_domain = {
695 kdc = $authentication->{AD_server}:88
696 admin_server = $authentication->{AD_server}:749
697 default_domain = $authentication->{DNS_domain}
698 }
699 EOF
700 domain_realm => <<EOF,
701 .$authentication->{DNS_domain} = $uc_domain
702 $authentication->{DNS_domain} = $uc_domain
703 EOF
704 kdc => <<'EOF',
705 profile = /etc/kerberos/krb5kdc/kdc.conf
706 EOF
707 pam => <<'EOF',
708 debug = false
709 ticket_lifetime = 36000
710 renew_lifetime = 36000
711 forwardable = true
712 krb4_convert = false
713 EOF
714 login => <<'EOF',
715 krb4_convert = false
716 krb4_get_tickets = false
717 EOF
718 );
719 foreach (group_by2(@sections)) {
720 my ($section, $txt) = @$_;
721 krb5_conf_overwrite_category($krb5_conf_file, $section => $authentication->{AD_server} ? $txt : '');
722 }
723 }
724
725 sub krb5_conf_overwrite_category {
726 my ($file, $category, $new_val) = @_;
727
728 my $done;
729 substInFile {
730 if (my $i = /^\s*\[\Q$category\E\]/i ... /^\[/) {
731 if ($new_val) {
732 if ($i == 1) {
733 $_ .= $new_val;
734 $done = 1;
735 } elsif ($i =~ /E/) {
736 $_ = "\n$_";
737 } else {
738 $_ = '';
739 }
740 } else {
741 $_ = '' if $i !~ /E/;
742 }
743 }
744 #- if category has not been found above.
745 if (eof && $new_val && !$done) {
746 $_ .= "\n[$category]\n$new_val";
747 }
748 } $file;
749 }
750
751 #- same as update_gnomekderc(), but allow spaces around "="
752 sub krb5_conf_update {
753 my ($file, $category, %subst_) = @_;
754
755 my %subst = map { lc($_) => [ $_, $subst_{$_} ] } keys %subst_;
756
757 my $s;
758 foreach (MDK::Common::File::cat_($file), "[NOCATEGORY]\n") {
759 if (my $i = /^\s*\[\Q$category\E\]/i ... /^\[/) {
760 if ($i =~ /E/) { #- for last line of category
761 chomp $s; $s .= "\n";
762 $s .= " $_->[0] = $_->[1]\n" foreach grep { defined($_->[1]) } values %subst;
763 %subst = ();
764 } elsif (/^\s*([^=]*?)\s*=/) {
765 if (my $e = delete $subst{lc($1)}) {
766 $_ = defined($e->[1]) ? " $1 = $e->[1]\n" : '';
767 }
768 }
769 }
770 $s .= $_ if !/^\Q[NOCATEGORY]/;
771 }
772
773 #- if category has not been found above.
774 if (keys %subst) {
775 chomp $s;
776 $s .= "\n[$category]\n";
777 $s .= " $_->[0] = $_->[1]\n" foreach grep { defined($_->[1]) } values %subst;
778 }
779
780 MDK::Common::File::output($file, $s);
781
782 }
783
784 sub sshd_config_UsePAM {
785 my ($UsePAM) = @_;
786 my $sshd = "$::prefix/etc/ssh/sshd_config";
787 -e $sshd or return;
788
789 my $val = "UsePAM " . bool2yesno($UsePAM);
790 substInFile {
791 $val = '' if s/^#?UsePAM.*/$val/;
792 $_ .= "$val\n" if eof && $val;
793 } $sshd;
794 }
795
796 sub query_srv_names {
797 my ($domain) = @_;
798
799 eval { require Net::DNS; 1 } or return;
800 my $res = Net::DNS::Resolver->new;
801 my $query = $res->query("_ldap._tcp.$domain", 'srv') or return;
802 map { $_->target } $query->answer;
803 }
804
805 sub enable_shadow() {
806 run_program::rooted($::prefix, "pwconv") or log::l("pwconv failed");
807 run_program::rooted($::prefix, "grpconv") or log::l("grpconv failed");
808 }
809
810 sub salt {
811 my ($nb) = @_;
812 require devices;
813 open(my $F, devices::make("urandom")) or die "missing urandom";
814 my $s; read $F, $s, $nb;
815 $s = pack("b8" x $nb, unpack "b6" x $nb, $s);
816 $s =~ tr|\0-\x3f|0-9a-zA-Z./|;
817 $s;
818 }
819
820 sub user_crypted_passwd {
821 my ($u, $authentication) = @_;
822 if ($u->{password}) {
823 require utf8;
824 utf8::encode($u->{password}); #- we don't want perl to do "smart" things in crypt()
825
826 crypt($u->{password},
827 !$authentication || $authentication->{blowfish} ? '$2a$08$' . salt(60) :
828 $authentication->{md5} ? '$1$' . salt(8) : salt(2));
829 } else {
830 $u->{pw} || '';
831 }
832 }
833
834 sub set_root_passwd {
835 my ($superuser, $authentication) = @_;
836 $superuser->{name} = 'root';
837 write_passwd_user($superuser, $authentication);
838 delete $superuser->{name};
839 }
840
841 sub write_passwd_user {
842 my ($u, $authentication) = @_;
843
844 $u->{pw} = user_crypted_passwd($u, $authentication);
845 $u->{shell} ||= '/bin/bash';
846
847 substInFile {
848 my $l = unpack_passwd($_);
849 if ($l->{name} eq $u->{name}) {
850 add2hash_($u, $l);
851 $_ = pack_passwd($u);
852 $u = {};
853 }
854 if (eof && $u->{name}) {
855 $_ .= pack_passwd($u);
856 }
857 } "$::prefix/etc/passwd";
858 }
859
860 my @etc_pass_fields = qw(name pw uid gid realname home shell);
861 sub unpack_passwd {
862 my ($l) = @_;
863 my %l; @l{@etc_pass_fields} = split ':', chomp_($l);
864 \%l;
865 }
866 sub pack_passwd {
867 my ($l) = @_;
868 join(':', @$l{@etc_pass_fields}) . "\n";
869 }
870
871 sub add_cafile() {
872 my $in = interactive->vnew;
873 $in->ask_filename({ title => N("Select file") }) or return;
874 }
875
876 sub auth() {
877 my $in = interactive->vnew;
878 $in->ask_from('', N(" "), [
879 { label => N("Domain Windows for authentication : ") . $authentication->{WINDOMAIN} },
880 {},
881 { label => N("Domain Admin User Name"), val => \$authentication->{winuser} },
882 { label => N("Domain Admin Password"), val => \$authentication->{winpass}, hidden => 1 },
883 ]);
884 return $authentication->{winuser}, $authentication->{winpass};
885 }
886
887 require fs::remote::smb;
888 sub list_domains() {
889 my $smb = fs::remote::smb->new;
890 my %domains;
891 foreach my $server ($smb->find_servers) {
892 $domains{$server->{group}} = 1;
893 }
894 return sort keys %domains;
895 }
896 sub get_server_for_domain {
897 my $smb = fs::remote::smb->new;
898 foreach my $server ($smb->find_servers) {
899 return $server->{name} if $server->{group} == $_[0];
900 }
901 }
902
903 sub fetch_dn {
904 my ($srv) = @_;
905 my $s = run_program::rooted_get_stdout($::prefix, 'ldapsearch', '-x', '-h', $srv, '-b', '', '-s', 'base', '+');
906 $authentication->{LDAPDOMAIN} = first($s =~ /namingContexts: (.+)/);
907 return $authentication->{LDAPDOMAIN};
908 }
909
910 sub configure_nss_ldap {
911 my ($authentication) = @_;
912 update_ldap_conf(
913 host => $authentication->{LDAP_server},
914 base => $authentication->{LDAPDOMAIN},
915 );
916
917 if ($authentication->{nssgrp} eq '1') {
918
919 update_ldap_conf(
920 nss_base_shadow => $authentication->{nss_shadow} . "?sub",
921 nss_base_passwd => $authentication->{nss_pwd} . "?sub",
922 nss_base_group => $authentication->{nss_grp} . "?sub",
923 );
924 } else {
925
926 update_ldap_conf(
927 nss_base_shadow => $authentication->{LDAPDOMAIN} . "?sub",
928 nss_base_passwd => $authentication->{LDAPDOMAIN} . "?sub",
929 nss_base_group => $authentication->{LDAPDOMAIN} . "?sub",
930 );
931 }
932 if ($authentication->{anonymous} eq '1') {
933 update_ldap_conf(
934 binddn => $authentication->{LDAP_binddn},
935 bindpw => $authentication->{LDAP_bindpwd},
936 );
937 }
938
939 if ($authentication->{cafile} eq '1') {
940 update_ldap_conf(
941 ssl => "on",
942 tls_checkpeer => "yes",
943 tls_cacertfile => $authentication->{file},
944 );
945 }
946 }
947
948 sub compute_password_weakness {
949
950 my ($password) = @_;
951 my $score = 0;
952 my $len = length($password);
953
954 return 0 if $len == 0;
955
956 $score = $len < 5 ? 3 :
957 $len > 4 && $len < 8 ? 6 :
958 $len > 7 && $len < 16 ? 12 : 18;
959
960 $score += 1 if $password =~ /[a-z]/;
961 $score += 5 if $password =~ /[A-Z]/;
962 $score += 5 if $password =~ /\d+/;
963 $score += 5 if $password =~ /(.*[0-9].*[0-9].*[0-9])/;
964 $score += 5 if $password =~ /.[!@#$%^&*?_~,]/;
965 $score += 5 if $password =~ /(.*[!@#$%^&*?_~,].*[!@#$%^&*?_~,])/;
966 $score += 2 if $password =~ /([a-z].*[A-Z])|([A-Z].*[a-z])/;
967 $score += 2 if $password =~ /([a-zA-Z])/ && $password =~ /([0-9])/;
968 $score += 2 if $password =~ /([a-z].*[A-Z])|([A-Z].*[a-z])/;
969 $score += 2 if $password =~ /([a-zA-Z0-9].*[!@#$%^&*?_~])|([!@#$%^&*?_~,].*[a-zA-Z0-9])/;
970
971 my $level = $score < 11 ? 1 :
972 $score > 10 && $score < 20 ? 2 :
973 $score > 19 && $score < 30 ? 3 :
974 $score > 29 && $score < 40 ? 4 : 5;
975
976 return $level;
977 }
978 1;

  ViewVC Help
Powered by ViewVC 1.1.26