Ignore:
Timestamp:
11/11/11 13:17:43 (19 months ago)
Author:
BrainSlayer
Message:

update proftp

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/router/proftpd/tests/t/lib/ProFTPD/Tests/Modules/mod_sql_passwd.pm

    r14674 r17876  
    22 
    33use lib qw(t/lib); 
    4 use base qw(Test::Unit::TestCase ProFTPD::TestSuite::Child); 
     4use base qw(ProFTPD::TestSuite::Child); 
    55use strict; 
    66 
    7 use File::Path qw(mkpath rmtree); 
    87use File::Spec; 
    98use IO::Handle; 
     
    107106  }, 
    108107 
     108  sql_passwd_md5_hash_encode_salt_password_bug3500 => { 
     109    order => ++$order, 
     110    test_class => [qw(bug forking)], 
     111  }, 
     112 
     113  sql_passwd_md5_hash_encode_password_bug3500 => { 
     114    order => ++$order, 
     115    test_class => [qw(bug forking)], 
     116  }, 
     117 
     118  sql_passwd_md5_rounds_bug3500 => { 
     119    order => ++$order, 
     120    test_class => [qw(bug forking)], 
     121  }, 
     122 
     123  sql_passwd_md5_rounds_hash_encode_salt_password_bug3500 => { 
     124    order => ++$order, 
     125    test_class => [qw(bug forking)], 
     126  }, 
     127 
     128  sql_passwd_md5_hash_password => { 
     129    order => ++$order, 
     130    test_class => [qw(forking)], 
     131  }, 
     132 
     133  sql_passwd_md5_hash_salt => { 
     134    order => ++$order, 
     135    test_class => [qw(forking)], 
     136  }, 
     137 
    109138}; 
    110139 
     
    115144sub list_tests { 
    116145  return testsuite_get_runnable_tests($TESTS); 
    117 } 
    118  
    119 sub set_up { 
    120   my $self = shift; 
    121   $self->{tmpdir} = testsuite_get_tmp_dir(); 
    122  
    123   # Create temporary scratch dir 
    124   eval { mkpath($self->{tmpdir}) }; 
    125   if ($@) { 
    126     my $abs_path = File::Spec->rel2abs($self->{tmpdir}); 
    127     die("Can't create dir $abs_path: $@"); 
    128   } 
    129 } 
    130  
    131 sub tear_down { 
    132   my $self = shift; 
    133  
    134   # Remove temporary scratch dir 
    135   if ($self->{tmpdir}) { 
    136     eval { rmtree($self->{tmpdir}) }; 
    137   } 
    138  
    139   undef $self; 
    140146} 
    141147 
     
    16861692        SQLPasswordEngine => 'on', 
    16871693        SQLPasswordEncoding => 'hex', 
    1688         SQLPasswordSaltFile => "$salt_file prepend", 
     1694        SQLPasswordSaltFile => "$salt_file Prepend", 
    16891695      }, 
    16901696    }, 
     
    30163022} 
    30173023 
     3024sub sql_passwd_md5_hash_encode_salt_password_bug3500 { 
     3025  my $self = shift; 
     3026  my $tmpdir = $self->{tmpdir}; 
     3027 
     3028  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3029  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3030  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3031 
     3032  my $log_file = File::Spec->rel2abs('tests.log'); 
     3033 
     3034  my $user = 'proftpd'; 
     3035 
     3036  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3037 
     3038  my $passwd = 'password'; 
     3039  my $salt = ':(Km-'; 
     3040 
     3041  # I used: 
     3042  # 
     3043  #  Digest::MD5::md5_hex(Digest::MD5::md5_hex($salt) . 
     3044  #                       Digest::MD5::md5_hex($passwd)); 
     3045  # 
     3046  # to generate this password. 
     3047  my $db_passwd = 'e434ada7d8d3db4924d3b2bfe3bf1ce4'; 
     3048 
     3049  my $group = 'ftpd'; 
     3050  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3051  my $uid = 500; 
     3052  my $gid = 500; 
     3053 
     3054  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3055 
     3056  # Build up sqlite3 command to create users, groups tables and populate them 
     3057  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3058 
     3059  if (open(my $fh, "> $db_script")) { 
     3060    print $fh <<EOS; 
     3061CREATE TABLE users ( 
     3062  userid TEXT, 
     3063  passwd TEXT, 
     3064  uid INTEGER, 
     3065  gid INTEGER, 
     3066  homedir TEXT,  
     3067  shell TEXT 
     3068); 
     3069INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3070 
     3071CREATE TABLE groups ( 
     3072  groupname TEXT, 
     3073  gid INTEGER, 
     3074  members TEXT 
     3075); 
     3076INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3077 
     3078CREATE TABLE user_salts ( 
     3079  userid TEXT, 
     3080  salt TEXT 
     3081); 
     3082INSERT INTO user_salts (userid, salt) VALUES ('$user', '$salt'); 
     3083EOS 
     3084 
     3085    unless (close($fh)) { 
     3086      die("Can't write $db_script: $!"); 
     3087    } 
     3088 
     3089  } else { 
     3090    die("Can't open $db_script: $!"); 
     3091  } 
     3092 
     3093  my $cmd = "sqlite3 $db_file < $db_script"; 
     3094 
     3095  if ($ENV{TEST_VERBOSE}) { 
     3096    print STDERR "Executing sqlite3: $cmd\n"; 
     3097  } 
     3098 
     3099  my @output = `$cmd`; 
     3100  if (scalar(@output) && 
     3101      $ENV{TEST_VERBOSE}) { 
     3102    print STDERR "Output: ", join('', @output), "\n"; 
     3103  } 
     3104 
     3105  my $config = { 
     3106    PidFile => $pid_file, 
     3107    ScoreboardFile => $scoreboard_file, 
     3108    SystemLog => $log_file, 
     3109 
     3110    IfModules => { 
     3111      'mod_delay.c' => { 
     3112        DelayEngine => 'off', 
     3113      }, 
     3114 
     3115      'mod_sql.c' => { 
     3116        SQLAuthTypes => 'md5', 
     3117        SQLBackend => 'sqlite3', 
     3118        SQLConnectInfo => $db_file, 
     3119        SQLLogFile => $log_file, 
     3120        SQLNamedQuery => 'get-user-salt SELECT "salt FROM user_salts WHERE userid = \'%{0}\'"', 
     3121        SQLMinID => '100', 
     3122      }, 
     3123 
     3124      'mod_sql_passwd.c' => { 
     3125        SQLPasswordEngine => 'on', 
     3126        SQLPasswordEncoding => 'hex', 
     3127        SQLPasswordUserSalt => 'sql:/get-user-salt Prepend', 
     3128 
     3129        # Tell mod_sql_passwd to transform both the salt and the password 
     3130        # before transforming the combination of them. 
     3131        SQLPasswordOptions => 'HashEncodeSalt HashEncodePassword', 
     3132      }, 
     3133    }, 
     3134  }; 
     3135 
     3136  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3137 
     3138  # Open pipes, for use between the parent and child processes.  Specifically, 
     3139  # the child will indicate when it's done with its test by writing a message 
     3140  # to the parent. 
     3141  my ($rfh, $wfh); 
     3142  unless (pipe($rfh, $wfh)) { 
     3143    die("Can't open pipe: $!"); 
     3144  } 
     3145 
     3146  my $ex; 
     3147 
     3148  # Fork child 
     3149  $self->handle_sigchld(); 
     3150  defined(my $pid = fork()) or die("Can't fork: $!"); 
     3151  if ($pid) { 
     3152    eval { 
     3153      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     3154      $client->login($user, $passwd); 
     3155 
     3156      my $resp_msgs = $client->response_msgs(); 
     3157      my $nmsgs = scalar(@$resp_msgs); 
     3158 
     3159      my $expected; 
     3160 
     3161      $expected = 1; 
     3162      $self->assert($expected == $nmsgs, 
     3163        test_msg("Expected $expected, got $nmsgs"));  
     3164 
     3165      $expected = "User proftpd logged in"; 
     3166      $self->assert($expected eq $resp_msgs->[0], 
     3167        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     3168 
     3169    }; 
     3170 
     3171    if ($@) { 
     3172      $ex = $@; 
     3173    } 
     3174 
     3175    $wfh->print("done\n"); 
     3176    $wfh->flush(); 
     3177 
     3178  } else { 
     3179    eval { server_wait($config_file, $rfh) }; 
     3180    if ($@) { 
     3181      warn($@); 
     3182      exit 1; 
     3183    } 
     3184 
     3185    exit 0; 
     3186  } 
     3187 
     3188  # Stop server 
     3189  server_stop($pid_file); 
     3190 
     3191  $self->assert_child_ok($pid); 
     3192 
     3193  if ($ex) { 
     3194    die($ex); 
     3195  } 
     3196 
     3197  unlink($log_file); 
     3198} 
     3199 
     3200sub sql_passwd_md5_hash_encode_password_bug3500 { 
     3201  my $self = shift; 
     3202  my $tmpdir = $self->{tmpdir}; 
     3203 
     3204  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3205  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3206  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3207 
     3208  my $log_file = File::Spec->rel2abs('tests.log'); 
     3209 
     3210  my $user = 'proftpd'; 
     3211 
     3212  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3213 
     3214  my $passwd = 'password'; 
     3215 
     3216  # I used: 
     3217  # 
     3218  #  Digest::MD5::md5_hex($passwd) 
     3219  # 
     3220  # to generate this password. 
     3221  my $db_passwd = '5f4dcc3b5aa765d61d8327deb882cf99'; 
     3222 
     3223  my $group = 'ftpd'; 
     3224  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3225  my $uid = 500; 
     3226  my $gid = 500; 
     3227 
     3228  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3229 
     3230  # Build up sqlite3 command to create users, groups tables and populate them 
     3231  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3232 
     3233  if (open(my $fh, "> $db_script")) { 
     3234    print $fh <<EOS; 
     3235CREATE TABLE users ( 
     3236  userid TEXT, 
     3237  passwd TEXT, 
     3238  uid INTEGER, 
     3239  gid INTEGER, 
     3240  homedir TEXT,  
     3241  shell TEXT 
     3242); 
     3243INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3244 
     3245CREATE TABLE groups ( 
     3246  groupname TEXT, 
     3247  gid INTEGER, 
     3248  members TEXT 
     3249); 
     3250INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3251 
     3252EOS 
     3253 
     3254    unless (close($fh)) { 
     3255      die("Can't write $db_script: $!"); 
     3256    } 
     3257 
     3258  } else { 
     3259    die("Can't open $db_script: $!"); 
     3260  } 
     3261 
     3262  my $cmd = "sqlite3 $db_file < $db_script"; 
     3263 
     3264  if ($ENV{TEST_VERBOSE}) { 
     3265    print STDERR "Executing sqlite3: $cmd\n"; 
     3266  } 
     3267 
     3268  my @output = `$cmd`; 
     3269  if (scalar(@output) && 
     3270      $ENV{TEST_VERBOSE}) { 
     3271    print STDERR "Output: ", join('', @output), "\n"; 
     3272  } 
     3273 
     3274  my $config = { 
     3275    PidFile => $pid_file, 
     3276    ScoreboardFile => $scoreboard_file, 
     3277    SystemLog => $log_file, 
     3278 
     3279    IfModules => { 
     3280      'mod_delay.c' => { 
     3281        DelayEngine => 'off', 
     3282      }, 
     3283 
     3284      'mod_sql.c' => { 
     3285        SQLAuthTypes => 'md5', 
     3286        SQLBackend => 'sqlite3', 
     3287        SQLConnectInfo => $db_file, 
     3288        SQLLogFile => $log_file, 
     3289        SQLMinID => '100', 
     3290      }, 
     3291 
     3292      'mod_sql_passwd.c' => { 
     3293        SQLPasswordEngine => 'on', 
     3294        SQLPasswordEncoding => 'hex', 
     3295 
     3296        # Tell mod_sql_passwd to transform the password 
     3297        # before transforming the combination of them. 
     3298        SQLPasswordOptions => 'HashEncodePassword', 
     3299      }, 
     3300    }, 
     3301  }; 
     3302 
     3303  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3304 
     3305  # Open pipes, for use between the parent and child processes.  Specifically, 
     3306  # the child will indicate when it's done with its test by writing a message 
     3307  # to the parent. 
     3308  my ($rfh, $wfh); 
     3309  unless (pipe($rfh, $wfh)) { 
     3310    die("Can't open pipe: $!"); 
     3311  } 
     3312 
     3313  my $ex; 
     3314 
     3315  # Fork child 
     3316  $self->handle_sigchld(); 
     3317  defined(my $pid = fork()) or die("Can't fork: $!"); 
     3318  if ($pid) { 
     3319    eval { 
     3320      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     3321      $client->login($user, $passwd); 
     3322 
     3323      my $resp_msgs = $client->response_msgs(); 
     3324      my $nmsgs = scalar(@$resp_msgs); 
     3325 
     3326      my $expected; 
     3327 
     3328      $expected = 1; 
     3329      $self->assert($expected == $nmsgs, 
     3330        test_msg("Expected $expected, got $nmsgs"));  
     3331 
     3332      $expected = "User proftpd logged in"; 
     3333      $self->assert($expected eq $resp_msgs->[0], 
     3334        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     3335 
     3336    }; 
     3337 
     3338    if ($@) { 
     3339      $ex = $@; 
     3340    } 
     3341 
     3342    $wfh->print("done\n"); 
     3343    $wfh->flush(); 
     3344 
     3345  } else { 
     3346    eval { server_wait($config_file, $rfh) }; 
     3347    if ($@) { 
     3348      warn($@); 
     3349      exit 1; 
     3350    } 
     3351 
     3352    exit 0; 
     3353  } 
     3354 
     3355  # Stop server 
     3356  server_stop($pid_file); 
     3357 
     3358  $self->assert_child_ok($pid); 
     3359 
     3360  if ($ex) { 
     3361    die($ex); 
     3362  } 
     3363 
     3364  unlink($log_file); 
     3365} 
     3366 
     3367sub sql_passwd_md5_rounds_bug3500 { 
     3368  my $self = shift; 
     3369  my $tmpdir = $self->{tmpdir}; 
     3370 
     3371  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3372  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3373  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3374 
     3375  my $log_file = File::Spec->rel2abs('tests.log'); 
     3376 
     3377  my $user = 'proftpd'; 
     3378 
     3379  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3380 
     3381  my $passwd = 'Password'; 
     3382  my $salt = 'Sav0ryS4lt'; 
     3383 
     3384  # I used: 
     3385  # 
     3386  #  Digest::MD5::md5_hex( 
     3387  #    Digest::MD5::md5_hex( 
     3388  #      Digest::MD5::md5_hex($salt . $passwd))); 
     3389  # 
     3390  # to generate this password. 
     3391  my $db_passwd = 'ca71e8bd1170c941633124ea4e424320'; 
     3392 
     3393  my $group = 'ftpd'; 
     3394  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3395  my $uid = 500; 
     3396  my $gid = 500; 
     3397 
     3398  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3399 
     3400  # Build up sqlite3 command to create users, groups tables and populate them 
     3401  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3402 
     3403  if (open(my $fh, "> $db_script")) { 
     3404    print $fh <<EOS; 
     3405CREATE TABLE users ( 
     3406  userid TEXT, 
     3407  passwd TEXT, 
     3408  uid INTEGER, 
     3409  gid INTEGER, 
     3410  homedir TEXT,  
     3411  shell TEXT 
     3412); 
     3413INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3414 
     3415CREATE TABLE groups ( 
     3416  groupname TEXT, 
     3417  gid INTEGER, 
     3418  members TEXT 
     3419); 
     3420INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3421 
     3422CREATE TABLE user_salts ( 
     3423  userid TEXT, 
     3424  salt TEXT 
     3425); 
     3426INSERT INTO user_salts (userid, salt) VALUES ('$user', '$salt'); 
     3427EOS 
     3428 
     3429    unless (close($fh)) { 
     3430      die("Can't write $db_script: $!"); 
     3431    } 
     3432 
     3433  } else { 
     3434    die("Can't open $db_script: $!"); 
     3435  } 
     3436 
     3437  my $cmd = "sqlite3 $db_file < $db_script"; 
     3438 
     3439  if ($ENV{TEST_VERBOSE}) { 
     3440    print STDERR "Executing sqlite3: $cmd\n"; 
     3441  } 
     3442 
     3443  my @output = `$cmd`; 
     3444  if (scalar(@output) && 
     3445      $ENV{TEST_VERBOSE}) { 
     3446    print STDERR "Output: ", join('', @output), "\n"; 
     3447  } 
     3448 
     3449  my $config = { 
     3450    PidFile => $pid_file, 
     3451    ScoreboardFile => $scoreboard_file, 
     3452    SystemLog => $log_file, 
     3453 
     3454    IfModules => { 
     3455      'mod_delay.c' => { 
     3456        DelayEngine => 'off', 
     3457      }, 
     3458 
     3459      'mod_sql.c' => { 
     3460        SQLAuthTypes => 'md5', 
     3461        SQLBackend => 'sqlite3', 
     3462        SQLConnectInfo => $db_file, 
     3463        SQLLogFile => $log_file, 
     3464        SQLNamedQuery => 'get-user-salt SELECT "salt FROM user_salts WHERE userid = \'%{0}\'"', 
     3465        SQLMinID => '100', 
     3466      }, 
     3467 
     3468      'mod_sql_passwd.c' => { 
     3469        SQLPasswordEngine => 'on', 
     3470        SQLPasswordEncoding => 'hex', 
     3471        SQLPasswordUserSalt => 'sql:/get-user-salt Prepend', 
     3472 
     3473        # Tell mod_sql_passwd to use three rounds of transformation 
     3474        SQLPasswordRounds => '3', 
     3475      }, 
     3476    }, 
     3477  }; 
     3478 
     3479  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3480 
     3481  # Open pipes, for use between the parent and child processes.  Specifically, 
     3482  # the child will indicate when it's done with its test by writing a message 
     3483  # to the parent. 
     3484  my ($rfh, $wfh); 
     3485  unless (pipe($rfh, $wfh)) { 
     3486    die("Can't open pipe: $!"); 
     3487  } 
     3488 
     3489  my $ex; 
     3490 
     3491  # Fork child 
     3492  $self->handle_sigchld(); 
     3493  defined(my $pid = fork()) or die("Can't fork: $!"); 
     3494  if ($pid) { 
     3495    eval { 
     3496      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     3497      $client->login($user, $passwd); 
     3498 
     3499      my $resp_msgs = $client->response_msgs(); 
     3500      my $nmsgs = scalar(@$resp_msgs); 
     3501 
     3502      my $expected; 
     3503 
     3504      $expected = 1; 
     3505      $self->assert($expected == $nmsgs, 
     3506        test_msg("Expected $expected, got $nmsgs"));  
     3507 
     3508      $expected = "User proftpd logged in"; 
     3509      $self->assert($expected eq $resp_msgs->[0], 
     3510        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     3511 
     3512    }; 
     3513 
     3514    if ($@) { 
     3515      $ex = $@; 
     3516    } 
     3517 
     3518    $wfh->print("done\n"); 
     3519    $wfh->flush(); 
     3520 
     3521  } else { 
     3522    eval { server_wait($config_file, $rfh) }; 
     3523    if ($@) { 
     3524      warn($@); 
     3525      exit 1; 
     3526    } 
     3527 
     3528    exit 0; 
     3529  } 
     3530 
     3531  # Stop server 
     3532  server_stop($pid_file); 
     3533 
     3534  $self->assert_child_ok($pid); 
     3535 
     3536  if ($ex) { 
     3537    die($ex); 
     3538  } 
     3539 
     3540  unlink($log_file); 
     3541} 
     3542 
     3543sub sql_passwd_md5_rounds_hash_encode_salt_password_bug3500 { 
     3544  my $self = shift; 
     3545  my $tmpdir = $self->{tmpdir}; 
     3546 
     3547  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3548  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3549  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3550 
     3551  my $log_file = File::Spec->rel2abs('tests.log'); 
     3552 
     3553  my $user = 'proftpd'; 
     3554 
     3555  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3556 
     3557  my $passwd = 'Password'; 
     3558  my $salt = 'Sav0ryS4lt'; 
     3559 
     3560  # I used: 
     3561  # 
     3562  #  Digest::MD5::md5_hex( 
     3563  #    Digest::MD5::md5_hex( 
     3564  #      Digest::MD5::md5_hex( 
     3565  #        Digest::MD5::md5_hex($salt) . Digest::MD5::md5_hex($passwd)))); 
     3566  # 
     3567  # to generate this password. 
     3568  my $db_passwd = 'b317dcd1738a96a608c27607dd8179c0'; 
     3569 
     3570  my $group = 'ftpd'; 
     3571  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3572  my $uid = 500; 
     3573  my $gid = 500; 
     3574 
     3575  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3576 
     3577  # Build up sqlite3 command to create users, groups tables and populate them 
     3578  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3579 
     3580  if (open(my $fh, "> $db_script")) { 
     3581    print $fh <<EOS; 
     3582CREATE TABLE users ( 
     3583  userid TEXT, 
     3584  passwd TEXT, 
     3585  uid INTEGER, 
     3586  gid INTEGER, 
     3587  homedir TEXT,  
     3588  shell TEXT 
     3589); 
     3590INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3591 
     3592CREATE TABLE groups ( 
     3593  groupname TEXT, 
     3594  gid INTEGER, 
     3595  members TEXT 
     3596); 
     3597INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3598 
     3599CREATE TABLE user_salts ( 
     3600  userid TEXT, 
     3601  salt TEXT 
     3602); 
     3603INSERT INTO user_salts (userid, salt) VALUES ('$user', '$salt'); 
     3604EOS 
     3605 
     3606    unless (close($fh)) { 
     3607      die("Can't write $db_script: $!"); 
     3608    } 
     3609 
     3610  } else { 
     3611    die("Can't open $db_script: $!"); 
     3612  } 
     3613 
     3614  my $cmd = "sqlite3 $db_file < $db_script"; 
     3615 
     3616  if ($ENV{TEST_VERBOSE}) { 
     3617    print STDERR "Executing sqlite3: $cmd\n"; 
     3618  } 
     3619 
     3620  my @output = `$cmd`; 
     3621  if (scalar(@output) && 
     3622      $ENV{TEST_VERBOSE}) { 
     3623    print STDERR "Output: ", join('', @output), "\n"; 
     3624  } 
     3625 
     3626  my $config = { 
     3627    PidFile => $pid_file, 
     3628    ScoreboardFile => $scoreboard_file, 
     3629    SystemLog => $log_file, 
     3630 
     3631    IfModules => { 
     3632      'mod_delay.c' => { 
     3633        DelayEngine => 'off', 
     3634      }, 
     3635 
     3636      'mod_sql.c' => { 
     3637        SQLAuthTypes => 'md5', 
     3638        SQLBackend => 'sqlite3', 
     3639        SQLConnectInfo => $db_file, 
     3640        SQLLogFile => $log_file, 
     3641        SQLNamedQuery => 'get-user-salt SELECT "salt FROM user_salts WHERE userid = \'%{0}\'"', 
     3642        SQLMinID => '100', 
     3643      }, 
     3644 
     3645      'mod_sql_passwd.c' => { 
     3646        SQLPasswordEngine => 'on', 
     3647        SQLPasswordEncoding => 'hex', 
     3648        SQLPasswordUserSalt => 'sql:/get-user-salt Prepend', 
     3649 
     3650        # Tell mod_sql_passwd to transform salt and password 
     3651        SQLPasswordOptions => 'HashEncodePassword HashEncodeSalt', 
     3652 
     3653        # Tell mod_sql_passwd to use three rounds of transformation 
     3654        SQLPasswordRounds => '3', 
     3655      }, 
     3656    }, 
     3657  }; 
     3658 
     3659  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3660 
     3661  # Open pipes, for use between the parent and child processes.  Specifically, 
     3662  # the child will indicate when it's done with its test by writing a message 
     3663  # to the parent. 
     3664  my ($rfh, $wfh); 
     3665  unless (pipe($rfh, $wfh)) { 
     3666    die("Can't open pipe: $!"); 
     3667  } 
     3668 
     3669  my $ex; 
     3670 
     3671  # Fork child 
     3672  $self->handle_sigchld(); 
     3673  defined(my $pid = fork()) or die("Can't fork: $!"); 
     3674  if ($pid) { 
     3675    eval { 
     3676      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     3677      $client->login($user, $passwd); 
     3678 
     3679      my $resp_msgs = $client->response_msgs(); 
     3680      my $nmsgs = scalar(@$resp_msgs); 
     3681 
     3682      my $expected; 
     3683 
     3684      $expected = 1; 
     3685      $self->assert($expected == $nmsgs, 
     3686        test_msg("Expected $expected, got $nmsgs"));  
     3687 
     3688      $expected = "User proftpd logged in"; 
     3689      $self->assert($expected eq $resp_msgs->[0], 
     3690        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     3691 
     3692    }; 
     3693 
     3694    if ($@) { 
     3695      $ex = $@; 
     3696    } 
     3697 
     3698    $wfh->print("done\n"); 
     3699    $wfh->flush(); 
     3700 
     3701  } else { 
     3702    eval { server_wait($config_file, $rfh) }; 
     3703    if ($@) { 
     3704      warn($@); 
     3705      exit 1; 
     3706    } 
     3707 
     3708    exit 0; 
     3709  } 
     3710 
     3711  # Stop server 
     3712  server_stop($pid_file); 
     3713 
     3714  $self->assert_child_ok($pid); 
     3715 
     3716  if ($ex) { 
     3717    die($ex); 
     3718  } 
     3719 
     3720  unlink($log_file); 
     3721} 
     3722 
     3723sub sql_passwd_md5_hash_password { 
     3724  my $self = shift; 
     3725  my $tmpdir = $self->{tmpdir}; 
     3726 
     3727  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3728  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3729  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3730 
     3731  my $log_file = File::Spec->rel2abs('tests.log'); 
     3732 
     3733  my $user = 'proftpd'; 
     3734 
     3735  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3736 
     3737  my $passwd = 'password'; 
     3738 
     3739  # I used: 
     3740  # 
     3741  #  Digest::MD5::md5_hex(Digest::MD5::md5($passwd)); 
     3742  # 
     3743  # to generate this password. 
     3744  my $db_passwd = '9bf4b3611c53176f5c649aa4fc1ff6b2'; 
     3745 
     3746  my $group = 'ftpd'; 
     3747  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3748  my $uid = 500; 
     3749  my $gid = 500; 
     3750 
     3751  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3752 
     3753  # Build up sqlite3 command to create users, groups tables and populate them 
     3754  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3755 
     3756  if (open(my $fh, "> $db_script")) { 
     3757    print $fh <<EOS; 
     3758CREATE TABLE users ( 
     3759  userid TEXT, 
     3760  passwd TEXT, 
     3761  uid INTEGER, 
     3762  gid INTEGER, 
     3763  homedir TEXT,  
     3764  shell TEXT 
     3765); 
     3766INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3767 
     3768CREATE TABLE groups ( 
     3769  groupname TEXT, 
     3770  gid INTEGER, 
     3771  members TEXT 
     3772); 
     3773INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3774 
     3775EOS 
     3776 
     3777    unless (close($fh)) { 
     3778      die("Can't write $db_script: $!"); 
     3779    } 
     3780 
     3781  } else { 
     3782    die("Can't open $db_script: $!"); 
     3783  } 
     3784 
     3785  my $cmd = "sqlite3 $db_file < $db_script"; 
     3786 
     3787  if ($ENV{TEST_VERBOSE}) { 
     3788    print STDERR "Executing sqlite3: $cmd\n"; 
     3789  } 
     3790 
     3791  my @output = `$cmd`; 
     3792  if (scalar(@output) && 
     3793      $ENV{TEST_VERBOSE}) { 
     3794    print STDERR "Output: ", join('', @output), "\n"; 
     3795  } 
     3796 
     3797  my $config = { 
     3798    PidFile => $pid_file, 
     3799    ScoreboardFile => $scoreboard_file, 
     3800    SystemLog => $log_file, 
     3801 
     3802    IfModules => { 
     3803      'mod_delay.c' => { 
     3804        DelayEngine => 'off', 
     3805      }, 
     3806 
     3807      'mod_sql.c' => { 
     3808        SQLAuthTypes => 'md5', 
     3809        SQLBackend => 'sqlite3', 
     3810        SQLConnectInfo => $db_file, 
     3811        SQLLogFile => $log_file, 
     3812        SQLMinID => '100', 
     3813      }, 
     3814 
     3815      'mod_sql_passwd.c' => { 
     3816        SQLPasswordEngine => 'on', 
     3817        SQLPasswordEncoding => 'hex', 
     3818 
     3819        SQLPasswordOptions => 'HashPassword', 
     3820      }, 
     3821    }, 
     3822  }; 
     3823 
     3824  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3825 
     3826  # Open pipes, for use between the parent and child processes.  Specifically, 
     3827  # the child will indicate when it's done with its test by writing a message 
     3828  # to the parent. 
     3829  my ($rfh, $wfh); 
     3830  unless (pipe($rfh, $wfh)) { 
     3831    die("Can't open pipe: $!"); 
     3832  } 
     3833 
     3834  my $ex; 
     3835 
     3836  # Fork child 
     3837  $self->handle_sigchld(); 
     3838  defined(my $pid = fork()) or die("Can't fork: $!"); 
     3839  if ($pid) { 
     3840    eval { 
     3841      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     3842      $client->login($user, $passwd); 
     3843 
     3844      my $resp_msgs = $client->response_msgs(); 
     3845      my $nmsgs = scalar(@$resp_msgs); 
     3846 
     3847      my $expected; 
     3848 
     3849      $expected = 1; 
     3850      $self->assert($expected == $nmsgs, 
     3851        test_msg("Expected $expected, got $nmsgs"));  
     3852 
     3853      $expected = "User proftpd logged in"; 
     3854      $self->assert($expected eq $resp_msgs->[0], 
     3855        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     3856 
     3857    }; 
     3858 
     3859    if ($@) { 
     3860      $ex = $@; 
     3861    } 
     3862 
     3863    $wfh->print("done\n"); 
     3864    $wfh->flush(); 
     3865 
     3866  } else { 
     3867    eval { server_wait($config_file, $rfh) }; 
     3868    if ($@) { 
     3869      warn($@); 
     3870      exit 1; 
     3871    } 
     3872 
     3873    exit 0; 
     3874  } 
     3875 
     3876  # Stop server 
     3877  server_stop($pid_file); 
     3878 
     3879  $self->assert_child_ok($pid); 
     3880 
     3881  if ($ex) { 
     3882    die($ex); 
     3883  } 
     3884 
     3885  unlink($log_file); 
     3886} 
     3887 
     3888sub sql_passwd_md5_hash_salt { 
     3889  my $self = shift; 
     3890  my $tmpdir = $self->{tmpdir}; 
     3891 
     3892  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     3893  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     3894  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     3895 
     3896  my $log_file = File::Spec->rel2abs('tests.log'); 
     3897 
     3898  my $user = 'proftpd'; 
     3899 
     3900  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     3901 
     3902  my $passwd = 'password'; 
     3903  my $salt = ':(Km-'; 
     3904 
     3905  # I used: 
     3906  # 
     3907  #  Digest::MD5::md5_hex(Digest::MD5::md5($salt) . $passwd); 
     3908  # 
     3909  # to generate this password. 
     3910  my $db_passwd = '6d829eb1782d1295a04a983da3d1286d'; 
     3911 
     3912  my $group = 'ftpd'; 
     3913  my $home_dir = File::Spec->rel2abs($tmpdir); 
     3914  my $uid = 500; 
     3915  my $gid = 500; 
     3916 
     3917  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     3918 
     3919  # Build up sqlite3 command to create users, groups tables and populate them 
     3920  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     3921 
     3922  if (open(my $fh, "> $db_script")) { 
     3923    print $fh <<EOS; 
     3924CREATE TABLE users ( 
     3925  userid TEXT, 
     3926  passwd TEXT, 
     3927  uid INTEGER, 
     3928  gid INTEGER, 
     3929  homedir TEXT,  
     3930  shell TEXT 
     3931); 
     3932INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     3933 
     3934CREATE TABLE groups ( 
     3935  groupname TEXT, 
     3936  gid INTEGER, 
     3937  members TEXT 
     3938); 
     3939INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     3940 
     3941CREATE TABLE user_salts ( 
     3942  userid TEXT, 
     3943  salt TEXT 
     3944); 
     3945INSERT INTO user_salts (userid, salt) VALUES ('$user', '$salt'); 
     3946EOS 
     3947 
     3948    unless (close($fh)) { 
     3949      die("Can't write $db_script: $!"); 
     3950    } 
     3951 
     3952  } else { 
     3953    die("Can't open $db_script: $!"); 
     3954  } 
     3955 
     3956  my $cmd = "sqlite3 $db_file < $db_script"; 
     3957 
     3958  if ($ENV{TEST_VERBOSE}) { 
     3959    print STDERR "Executing sqlite3: $cmd\n"; 
     3960  } 
     3961 
     3962  my @output = `$cmd`; 
     3963  if (scalar(@output) && 
     3964      $ENV{TEST_VERBOSE}) { 
     3965    print STDERR "Output: ", join('', @output), "\n"; 
     3966  } 
     3967 
     3968  my $config = { 
     3969    PidFile => $pid_file, 
     3970    ScoreboardFile => $scoreboard_file, 
     3971    SystemLog => $log_file, 
     3972 
     3973    IfModules => { 
     3974      'mod_delay.c' => { 
     3975        DelayEngine => 'off', 
     3976      }, 
     3977 
     3978      'mod_sql.c' => { 
     3979        SQLAuthTypes => 'md5', 
     3980        SQLBackend => 'sqlite3', 
     3981        SQLConnectInfo => $db_file, 
     3982        SQLLogFile => $log_file, 
     3983        SQLNamedQuery => 'get-user-salt SELECT "salt FROM user_salts WHERE userid = \'%{0}\'"', 
     3984        SQLMinID => '100', 
     3985      }, 
     3986 
     3987      'mod_sql_passwd.c' => { 
     3988        SQLPasswordEngine => 'on', 
     3989        SQLPasswordEncoding => 'hex', 
     3990        SQLPasswordUserSalt => 'sql:/get-user-salt Prepend', 
     3991 
     3992        SQLPasswordOptions => 'HashSalt', 
     3993      }, 
     3994    }, 
     3995  }; 
     3996 
     3997  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     3998 
     3999  # Open pipes, for use between the parent and child processes.  Specifically, 
     4000  # the child will indicate when it's done with its test by writing a message 
     4001  # to the parent. 
     4002  my ($rfh, $wfh); 
     4003  unless (pipe($rfh, $wfh)) { 
     4004    die("Can't open pipe: $!"); 
     4005  } 
     4006 
     4007  my $ex; 
     4008 
     4009  # Fork child 
     4010  $self->handle_sigchld(); 
     4011  defined(my $pid = fork()) or die("Can't fork: $!"); 
     4012  if ($pid) { 
     4013    eval { 
     4014      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     4015      $client->login($user, $passwd); 
     4016 
     4017      my $resp_msgs = $client->response_msgs(); 
     4018      my $nmsgs = scalar(@$resp_msgs); 
     4019 
     4020      my $expected; 
     4021 
     4022      $expected = 1; 
     4023      $self->assert($expected == $nmsgs, 
     4024        test_msg("Expected $expected, got $nmsgs"));  
     4025 
     4026      $expected = "User proftpd logged in"; 
     4027      $self->assert($expected eq $resp_msgs->[0], 
     4028        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     4029 
     4030    }; 
     4031 
     4032    if ($@) { 
     4033      $ex = $@; 
     4034    } 
     4035 
     4036    $wfh->print("done\n"); 
     4037    $wfh->flush(); 
     4038 
     4039  } else { 
     4040    eval { server_wait($config_file, $rfh) }; 
     4041    if ($@) { 
     4042      warn($@); 
     4043      exit 1; 
     4044    } 
     4045 
     4046    exit 0; 
     4047  } 
     4048 
     4049  # Stop server 
     4050  server_stop($pid_file); 
     4051 
     4052  $self->assert_child_ok($pid); 
     4053 
     4054  if ($ex) { 
     4055    die($ex); 
     4056  } 
     4057 
     4058  unlink($log_file); 
     4059} 
     4060 
     4061sub sql_passwd_md5_encode_salt { 
     4062  my $self = shift; 
     4063  my $tmpdir = $self->{tmpdir}; 
     4064 
     4065  my $config_file = "$tmpdir/sqlpasswd.conf"; 
     4066  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.pid"); 
     4067  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlpasswd.scoreboard"); 
     4068 
     4069  my $log_file = File::Spec->rel2abs('tests.log'); 
     4070 
     4071  my $user = 'proftpd'; 
     4072 
     4073  # See http://bugs.proftpd.org/show_bug.cgi?id=3500 for more details 
     4074 
     4075  my $passwd = 'password'; 
     4076  my $salt = ':(Km-'; 
     4077 
     4078  # I used: 
     4079  # 
     4080  #  Digest::MD5::md5_hex(Digest::MD5::md5_hex($salt) . 
     4081  #                       Digest::MD5::md5_hex($passwd)); 
     4082  # 
     4083  # to generate this password. 
     4084  my $db_passwd = 'e434ada7d8d3db4924d3b2bfe3bf1ce4'; 
     4085 
     4086  my $group = 'ftpd'; 
     4087  my $home_dir = File::Spec->rel2abs($tmpdir); 
     4088  my $uid = 500; 
     4089  my $gid = 500; 
     4090 
     4091  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db"); 
     4092 
     4093  # Build up sqlite3 command to create users, groups tables and populate them 
     4094  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql"); 
     4095 
     4096  if (open(my $fh, "> $db_script")) { 
     4097    print $fh <<EOS; 
     4098CREATE TABLE users ( 
     4099  userid TEXT, 
     4100  passwd TEXT, 
     4101  uid INTEGER, 
     4102  gid INTEGER, 
     4103  homedir TEXT,  
     4104  shell TEXT 
     4105); 
     4106INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$db_passwd', $uid, $gid, '$home_dir', '/bin/bash'); 
     4107 
     4108CREATE TABLE groups ( 
     4109  groupname TEXT, 
     4110  gid INTEGER, 
     4111  members TEXT 
     4112); 
     4113INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user'); 
     4114 
     4115CREATE TABLE user_salts ( 
     4116  userid TEXT, 
     4117  salt TEXT 
     4118); 
     4119INSERT INTO user_salts (userid, salt) VALUES ('$user', '$salt'); 
     4120EOS 
     4121 
     4122    unless (close($fh)) { 
     4123      die("Can't write $db_script: $!"); 
     4124    } 
     4125 
     4126  } else { 
     4127    die("Can't open $db_script: $!"); 
     4128  } 
     4129 
     4130  my $cmd = "sqlite3 $db_file < $db_script"; 
     4131 
     4132  if ($ENV{TEST_VERBOSE}) { 
     4133    print STDERR "Executing sqlite3: $cmd\n"; 
     4134  } 
     4135 
     4136  my @output = `$cmd`; 
     4137  if (scalar(@output) && 
     4138      $ENV{TEST_VERBOSE}) { 
     4139    print STDERR "Output: ", join('', @output), "\n"; 
     4140  } 
     4141 
     4142  my $config = { 
     4143    PidFile => $pid_file, 
     4144    ScoreboardFile => $scoreboard_file, 
     4145    SystemLog => $log_file, 
     4146 
     4147    IfModules => { 
     4148      'mod_delay.c' => { 
     4149        DelayEngine => 'off', 
     4150      }, 
     4151 
     4152      'mod_sql.c' => { 
     4153        SQLAuthTypes => 'md5', 
     4154        SQLBackend => 'sqlite3', 
     4155        SQLConnectInfo => $db_file, 
     4156        SQLLogFile => $log_file, 
     4157        SQLNamedQuery => 'get-user-salt SELECT "salt FROM user_salts WHERE userid = \'%{0}\'"', 
     4158        SQLMinID => '100', 
     4159      }, 
     4160 
     4161      'mod_sql_passwd.c' => { 
     4162        SQLPasswordEngine => 'on', 
     4163        SQLPasswordEncoding => 'hex', 
     4164        SQLPasswordUserSalt => 'sql:/get-user-salt Prepend', 
     4165 
     4166        # Tell mod_sql_passwd to transform both the salt and the password 
     4167        # before transforming the combination of them. 
     4168        SQLPasswordOptions => 'HashEncodeSalt HashEncodePassword', 
     4169      }, 
     4170    }, 
     4171  }; 
     4172 
     4173  my ($port, $config_user, $config_group) = config_write($config_file, $config); 
     4174 
     4175  # Open pipes, for use between the parent and child processes.  Specifically, 
     4176  # the child will indicate when it's done with its test by writing a message 
     4177  # to the parent. 
     4178  my ($rfh, $wfh); 
     4179  unless (pipe($rfh, $wfh)) { 
     4180    die("Can't open pipe: $!"); 
     4181  } 
     4182 
     4183  my $ex; 
     4184 
     4185  # Fork child 
     4186  $self->handle_sigchld(); 
     4187  defined(my $pid = fork()) or die("Can't fork: $!"); 
     4188  if ($pid) { 
     4189    eval { 
     4190      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 
     4191      $client->login($user, $passwd); 
     4192 
     4193      my $resp_msgs = $client->response_msgs(); 
     4194      my $nmsgs = scalar(@$resp_msgs); 
     4195 
     4196      my $expected; 
     4197 
     4198      $expected = 1; 
     4199      $self->assert($expected == $nmsgs, 
     4200        test_msg("Expected $expected, got $nmsgs"));  
     4201 
     4202      $expected = "User proftpd logged in"; 
     4203      $self->assert($expected eq $resp_msgs->[0], 
     4204        test_msg("Expected '$expected', got '$resp_msgs->[0]'")); 
     4205 
     4206    }; 
     4207 
     4208    if ($@) { 
     4209      $ex = $@; 
     4210    } 
     4211 
     4212    $wfh->print("done\n"); 
     4213    $wfh->flush(); 
     4214 
     4215  } else { 
     4216    eval { server_wait($config_file, $rfh) }; 
     4217    if ($@) { 
     4218      warn($@); 
     4219      exit 1; 
     4220    } 
     4221 
     4222    exit 0; 
     4223  } 
     4224 
     4225  # Stop server 
     4226  server_stop($pid_file); 
     4227 
     4228  $self->assert_child_ok($pid); 
     4229 
     4230  if ($ex) { 
     4231    die($ex); 
     4232  } 
     4233 
     4234  unlink($log_file); 
     4235} 
     4236 
    301842371; 
Note: See TracChangeset for help on using the changeset viewer.