/tools/jabberd-authpipe-pam-0.1.pl

https://github.com/eagafonov/jabberd2 · Perl · 227 lines · 132 code · 50 blank · 45 comment · 17 complexity · b8d14e69e754ddd1c0a05fcd9d1194b9 MD5 · raw file

  1. #!/usr/bin/perl -w
  2. #
  3. #
  4. # jabberd-authpipe-pam.pl version 0.1
  5. # Allows Jabber authentication against PAM without running jabberd as root.
  6. #
  7. # Copyright 2006 Nicholas J Humfrey
  8. # This code is hereby placed into the public domain.
  9. #
  10. #
  11. # Configure jabberd2 by editing c2s.xml:
  12. #
  13. # <module>pipe</module>
  14. #
  15. # <pipe>
  16. # <!-- Program to execute -->
  17. # <exec>/usr/local/libexec/jabberd-authpipe-pam.pl</exec>
  18. # </pipe>
  19. #
  20. # Place this script in /usr/local/libexec then run:
  21. # chown root jabberd-authpipe-pam.pl
  22. # chmod 4755 jabberd-authpipe-pam.pl
  23. #
  24. # This sets the script to run as suid root and gives it access to the
  25. # shadow password file. You man need to install perl-suid in order to
  26. # run perl scripts with the suid bit set.
  27. #
  28. # This script only implements the User-Exists and Check-Password routines.
  29. # See docs/dev/c2s-pipe-authenticator for details about the protocol.
  30. #
  31. #
  32. # To get this script to work with PSI, Ifound that I had to enable
  33. # "Allow Plaintext Login" in PSI. Please make sure that you use SSL
  34. # so that plain-text passwords aren't sent over the network.
  35. #
  36. #
  37. use strict;
  38. use MIME::Base64;
  39. use Authen::PAM qw(:constants);
  40. ### Start of Settings ###
  41. my $SERVICE_NAME = 'jabberd';
  42. my $DEBUG = 0;
  43. ### End of Settings #####
  44. open(STDERR, ">/tmp/jabber-authpipe.log") or die "Failed to open log";
  45. # Flush output immediately.
  46. $| = 1;
  47. # On startup, we have to inform c2s of the functions we can deal with.
  48. print "OK USER-EXISTS CHECK-PASSWORD FREE\n";
  49. # Our main loop
  50. my $buf;
  51. while(sysread (STDIN, $buf, 1024) > 0)
  52. {
  53. my ($cmd, @args) = split ' ', $buf;
  54. $cmd =~ tr/[a-z]/[A-Z]/;
  55. $cmd =~ tr/_/-/;
  56. if ($cmd eq 'USER-EXISTS') {
  57. print cmd_user_exists( @args ), "\n";
  58. } elsif ($cmd eq 'CHECK-PASSWORD') {
  59. print cmd_check_password( @args ), "\n";
  60. } elsif ($cmd eq 'FREE') {
  61. # c2s shutting down, do the same.
  62. last;
  63. } else {
  64. print STDERR "Unsupported command: '$cmd'\n" if ($DEBUG);
  65. print "NO\n";
  66. }
  67. }
  68. # Determine if the requested user exists.
  69. sub cmd_user_exists
  70. {
  71. my ($user, $realm) = @_;
  72. my ($name,$passwd,$uid,$gid) = getpwnam($user);
  73. if (defined $name) {
  74. print STDERR "User '$user' exists with ID $uid.\n" if ($DEBUG);
  75. return "OK";
  76. } else {
  77. print STDERR "User '$user' does not exist.\n" if ($DEBUG);
  78. return 'NO';
  79. }
  80. }
  81. # Compare the given password with the stored password.
  82. sub cmd_check_password
  83. {
  84. my ($username, $encoded_pass, $realm) = @_;
  85. # Decode the password
  86. my $password = decode_base64($encoded_pass);
  87. return "NO" if not $password;
  88. my $handler = sub {
  89. my @response = ();
  90. while (@_) {
  91. my $code = shift;
  92. my $message = shift;
  93. my $answer = undef;
  94. if ( $code == PAM_PROMPT_ECHO_ON ) {
  95. $answer = $username;
  96. }
  97. if ( $code == PAM_PROMPT_ECHO_OFF ) {
  98. $answer = $password;
  99. }
  100. push( @response, PAM_SUCCESS, $answer );
  101. }
  102. return ( @response, PAM_SUCCESS );
  103. };
  104. my $pam = Authen::PAM->new( $SERVICE_NAME, $username, $handler );
  105. unless ( ref $pam ) {
  106. my $error = Authen::PAM->pam_strerror($pam);
  107. print STDERR "Failed to authenticate user '$username' using service '$SERVICE_NAME'. Reason: '$error'\n";
  108. return 'NO';
  109. }
  110. my $result = $pam->pam_authenticate;
  111. unless ( $result == PAM_SUCCESS ) {
  112. my $error = $pam->pam_strerror($result);
  113. print STDERR "Failed to authenticate user '$username' using service '$SERVICE_NAME'. Reason: '$error'\n";
  114. return 'NO';
  115. }
  116. $result = $pam->pam_acct_mgmt;
  117. unless ( $result == PAM_SUCCESS ) {
  118. my $error = $pam->pam_strerror($result);
  119. print STDERR "Failed to authenticate user '$username' using service '$SERVICE_NAME'. Reason: '$error'\n";
  120. return 'NO';
  121. }
  122. print STDERR "Successfully authenticated user '$username' using service '$SERVICE_NAME'.\n" if ($DEBUG);
  123. return 'OK';
  124. }
  125. __END__
  126. =head1 NAME
  127. jabberd-authpipe-pam - Allows Jabber authentication against PAM without running jabberd as root.
  128. =head1 VERSION
  129. This document describes version 0.1 of jabberd-authpipe-pam, released 25th December 2006.
  130. =head1 DESCRIPTION
  131. Configure jabberd2 by editing c2s.xml:
  132. <module>pipe</module>
  133. <pipe>
  134. <!-- Program to execute -->
  135. <exec>/usr/local/libexec/jabberd-authpipe-pam.pl</exec>
  136. </pipe>
  137. Place this script in /usr/local/libexec then run:
  138. chown root jabberd-authpipe-pam.pl
  139. chmod 4755 jabberd-authpipe-pam.pl
  140. This sets the script to run as suid root and gives it access to the
  141. shadow password file. You man need to install perl-suid in order to
  142. run perl scripts with the suid bit set.
  143. This script only implements the User-Exists and Check-Password routines.
  144. See docs/dev/c2s-pipe-authenticator for details about the protocol.
  145. To get this script to work with PSI, Ifound that I had to enable
  146. "Allow Plaintext Login" in PSI. Please make sure that you use SSL
  147. so that plain-text passwords aren't sent over the network.
  148. =head1 README
  149. This script allows Jabber authentication against PAM without running jabberd as root.
  150. =head1 PREREQUISITES
  151. This script requires the Jabberd 2.0 server.
  152. It also requires the following other modules from CPAN: C<MIME-Base64> and C<Authen::PAM>.
  153. =pod OSNAMES
  154. Linux
  155. =pod SCRIPT CATEGORIES
  156. Misc
  157. =head1 AUTHOR
  158. Nicholas Humfrey E<lt>njh@aelius.comE<gt>
  159. =head1 COPYRIGHT
  160. Copyright (c) 2006, Nicholas J Humfrey
  161. This code is hereby placed into the public domain.
  162. =cut