[Techtalk] Perl Sockets Communication

Kai MacTane kmactane at GothPunk.com
Wed Aug 22 14:18:18 EST 2001

At 8/21/01 08:20 AM , Miller, Hoff wrote:
>I've got some experience with this too. Post your code and try to include a
>transcript of your shell session showing the commands and  error messages.

Okay, here's the basic scoop: my client wants to allow users to change 
their passwords via the Web. changepw.pl is the CGI script run by Apache; 
it takes in form fields userid, oldpasswd, newpasswd1 and newpasswd2. 
changepw.pl does not yet bother to make sure that newpasswd1 and newpasswd2 
match, but that will be an eventual part of it. After that basic check, it 
should then hand off the values of username, old_pass, and new_pass, via 
the /tmp/pwchanged_sock socket, to the pwchanged daemon process. This 
process runs as root, and checks that user [username]'s current password is 
indeed [old_pass]. (If it's not, it transmits an error message back through 
the socket.) pwchanged then tries to actually change the password, again 
returning a success or failure message through the socket. changepw.pl 
should display the returned message in HTML.

Here's the code for both scripts, then some commentary on the behavior I've 
been seeing...


# changepw.pl
# CGI script to change user password via HTTP.

require "cgi-lib.pl";
require 5.002;
use Socket;
# use strict;

$username = $input{'userid'};
$oldpass = $input{'oldpasswd'};
$newpass = $input{'newpasswd1'};

print "Content-Type: text/html\n\n";

print <<EOF;
<TITLE>Testing Socket Connection...</title>

<BODY bgcolor="#FFFFFF">

<H1>Testing Socket Connection...</h1>


my $socket_file = '/tmp/pwchanged_sock';

print "Opening socket file $socket_file...";

socket(SOCK, PF_UNIX, SOCK_STREAM, 0)      or die "Can't open socket: $!";
connect(SOCK, sockaddr_un($socket_file))   or die "Can't connect: $!";

print "Suceeded!<BR>\n";

### Note: have tried both print and send in the next couple of lines;
### no change in observed script behavior.

#print SOCK "Username: $username; Old Password: $old_pass; New Password: 

send SOCK, "Username: $username; Old Password: $old_pass; New Password: 
$new_pass\n", 0;

print "Sent socket message.<BR>\n";

while (my $line = <SOCK>)
     chomp $line;
     print "Server response: $line<BR>\n";

print "<P>Run complete!\n\n</body>\n</html>\n\n";

exit 0;


# pwchanged
# Password Change Daemon
# This daemon should run as root and accept socket connections
# from CGI scripts, allowing users to change their passwords
# via the Web.

use strict;
use Socket;
use Unix::Syslog qw(:macros);  # Syslog macros
use Unix::Syslog qw(:subs);    # Syslog functions

# use MD5;

my ($syslog_facility, $syslog_options, $syslog_priority);
my ($username, $old_pass, $new_pass);

# Set a variety of hard-coded configuration options.

# What syslog facility should we use?
# Using 'authpriv' keeps messages from being logged anywhere
# except /var/log/secure.
$syslog_facility = '1<<3';   # equals 'user' facility.

# What syslog priority should we use?
$syslog_priority = '6';      # equals 'info' level.

# Any syslog options?
$syslog_options = 'LOG_PID';

# Where should the socket file go?
my $socket_file = '/tmp/pwchanged_sock';

# And how many connections should we queue?
my $max_conn = 5;

# End of hard-coded variables.

# Now write something to the syslog to say that you've started up.
&write_syslog('Starting password change daemon.');

# Check that socket file exists and is world-writable.
### (Yet to be written).

# Now fork the daemon process into the background.
unless (fork)
     unless (fork)
         # We should now be happily in the background,
         # running our daemon process.

         # Write something into the syslog to that effect.
#       &write_syslog('Daemon process now forked.');

         while (1)
             # Now you need to wait for sockets to be formed
             # by the CGI script client.

             my $uaddr = sockaddr_un($socket_file);
             my $proto = getprotobyname('tcp');

             socket(Sock, PF_UNIX, SOCK_STREAM, 0)    or die "No socket: $!";
             bind(Sock, $uaddr);
             listen(Sock, 5);
             chmod 0777, $socket_file;

             &write_syslog('Daemon listening on %s for <= %d connections.', 
$socket_file, $max_conn);

             # (Then some kind of parsing on incoming msgs...)

             &write_syslog('Entering main FOR loop.');
             for ( ; my $paddr = accept(Client, Sock); close Client)
                 print CLIENT "Printing to client filehandle.\n";

                 my $line = <CLIENT>;
                 chomp $line;

                 &write_syslog('Client connection; content: "%s"', $line);

                 ($username, $old_pass, $new_pass) = split(/\;\s*/, $line);
                 if ($username =~ /^Username:\s*/)
                     $username = $';
                 } else {
                     print CLIENT "Invalid calling syntax!\n";
                     &write_syslog('Invalid calling syntax from client; 
dropping connection.');
                     next LOOP;

if ($old_pass =~ /^Old Password:\s*/)
                     $old_pass = $';
                 } else {
                     print CLIENT "Invalid calling syntax!\n";
                     &write_syslog('Invalid calling syntax from client; 
dropping connection.');
                     next LOOP;

                 if ($new_pass =~ /^New Password:\s*/)
                     $new_pass = $';
                 } else {
                     print CLIENT "Invalid calling syntax!\n";
                     &write_syslog('Invalid calling syntax from client; 
dropping connection.');
                     next LOOP;

                 # Now, presume you have $username, $old_pass,
                 # and $new_pass. (The CGI script has checked
                 # that the user entered the same value for
                 # $new_pass twice.)

                 # Make sure the user exists.

                 unless (`grep ^$username\: /etc/passwd`)
                     print CLIENT "No such user: $username. Aborting.\n";
                     &write_syslog('Invalid user "%s"; dropping 
connection.', $username);
                     next LOOP;

                 if ($new_pass eq $old_pass)
                     print CLIENT "New password must be different from old 
                     &write_syslog('Error: old and new passwords matched 
for user %s', $username);
                     next LOOP;

                 # Make sure we don't get in a tight loop.
                 sleep 5;
             &write_syslog('Exited main FOR loop.');

         &write_syslog('Exiting at point 1.');
         exit 0;
     #&write_syslog('Exiting at point 2.');
     exit 0;


sub write_syslog {

     my $format = shift;
     openlog('pwchanged', $syslog_options, $syslog_facility);
     syslog($syslog_priority, $format, @_);


Now, when I start up pwchanged, it writes into the syslog nicely, giving me 
log entries like:

Aug  9 13:23:23 mail pwchanged: Starting password change daemon.
Aug  9 13:23:23 mail pwchanged: Daemon listening on /tmp/pwchanged_sock for 
<= 5 connections.
Aug  9 13:23:23 mail pwchanged: Entering main FOR loop.

But then when I try running changepw.pl from a Web browser, I get:

Aug  9 13:23:43 mail pwchanged: Client connection; content: ""
Aug  9 13:23:43 mail pwchanged: Invalid user ""; dropping connection.

The output that's returned to my Web browser by changepw.pl looks like this:

    Testing Socket Connection...
    Opening socket file /tmp/pwchanged_sock...Suceeded!
    Sent socket message.

    Run complete!

So, changepw.pl thinks it's sending a message. pwchanged agrees that it's 
noticing an incoming message. But it thinks that message has no content.

Can anyone tell me what's going wrong here? Thanks!

                                                 --Kai MacTane
"And the Devil in a black dress watches over,
  My guardian angel walks away..."
                                                 --Sisters of Mercy,
                                                  "Temple of Love"

More information about the Techtalk mailing list