Add ud-krb-reset, and make ud-mailgate call it when receiving a mail at chpasswd...
[mirror/userdir-ldap.git] / ud-mailgate
index e8b509a..a37b047 100755 (executable)
@@ -9,6 +9,7 @@
 
 import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, commands
 import pwd, tempfile
+import subprocess
 
 from userdir_gpg import *
 from userdir_ldap import *
@@ -383,7 +384,7 @@ def DoDNS(Str,Attrs,DnRecord):
    if cnamerecord is None and\
       arecord is None and\
       mxrecord is None and\
-      txtrecord is None and
+      txtrecord is None and\
       aaaarecord is None:
      return None;
 
@@ -554,6 +555,22 @@ def FinishConfirmSudopassword(l, uid, Attrs):
 
    return result
 
+def connect_to_ldap_and_check_if_locked(DnRecord):
+   # Connect to the ldap server
+   l = connectLDAP()
+   F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
+   AccessPass = F.readline().strip().split(" ")
+   F.close();
+   l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
+
+   # Check for a locked account
+   Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
+   if (GetAttr(Attrs[0],"userPassword").find("*LK*") != -1) \
+             or GetAttr(Attrs[0],"userPassword").startswith("!"):
+      raise UDNotAllowedError, "This account is locked";
+
+   return l
+
 # Handle an [almost] arbitary change
 def HandleChange(Reply,DnRecord,Key):
    global PlainText;
@@ -592,16 +609,7 @@ def HandleChange(Reply,DnRecord,Key):
       Result = Result + Res + "\n";
 
    # Connect to the ldap server
-   l = connectLDAP()
-   F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
-   AccessPass = F.readline().strip().split(" ")
-   F.close();
-
-   l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
-   oldAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
-   if ((GetAttr(oldAttrs[0],"userPassword").find("*LK*") != -1) 
-       or GetAttr(oldAttrs[0],"userPassword").startswith("!")):
-      raise UDNotAllowedError, "This account is locked";
+   l = connect_to_ldap_and_check_if_locked(DnRecord)
 
    if CommitChanges == 1: # only if we are still good to go
       try:
@@ -643,13 +651,23 @@ def HandlePing(Reply,DnRecord,Key):
 
    return Reply + TemplateSubst(Subst,open(TemplatesDir+"ping-reply","r").read());
 
+
+
+def get_crypttype_preamble(key):
+   if (key[4] == 1):
+      type = "Your message was encrypted using PGP 2.x\ncompatibility mode.";
+   else:
+      type = "Your message was encrypted using GPG (OpenPGP)\ncompatibility "\
+             "mode, without IDEA. This message cannot be decoded using PGP 2.x";
+   return type
+
 # Handle a change password email sent to the change password address
 # (this program called with the chpass argument)
 def HandleChPass(Reply,DnRecord,Key):
    # Generate a random password
    Password = GenPass();
    Pass = HashPass(Password);
-      
+
    # Use GPG to encrypt it      
    Message = GPGEncrypt("Your new password is '" + Password + "'\n",\
                         "0x"+Key[1],Key[4]);
@@ -658,33 +676,15 @@ def HandleChPass(Reply,DnRecord,Key):
    if Message == None:
       raise UDFormatError, "Unable to generate the encrypted reply, gpg failed.";
 
-   if (Key[4] == 1):
-      Type = "Your message was encrypted using PGP 2.x\ncompatibility mode.";
-   else:
-      Type = "Your message was encrypted using GPG (OpenPGP)\ncompatibility "\
-             "mode, without IDEA. This message cannot be decoded using PGP 2.x";
-   
    Subst = {};
    Subst["__FROM__"] = ChPassFrom;
    Subst["__EMAIL__"] = EmailAddress(DnRecord);
-   Subst["__CRYPTTYPE__"] = Type;
+   Subst["__CRYPTTYPE__"] = get_crypttype_preamble(Key)
    Subst["__PASSWORD__"] = Message;
    Subst["__ADMIN__"] = ReplyTo;
    Reply = Reply + TemplateSubst(Subst,open(TemplatesDir+"passwd-changed","r").read());
-   
-   # Connect to the ldap server
-   l = connectLDAP()
-   F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r");
-   AccessPass = F.readline().strip().split(" ")
-   F.close();
-   l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]);
-
-   # Check for a locked account
-   Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid"));
-   if (GetAttr(Attrs[0],"userPassword").find("*LK*") != -1) \
-             or GetAttr(Attrs[0],"userPassword").startswith("!"):
-      raise UDNotAllowedError, "This account is locked";
 
+   l = connect_to_ldap_and_check_if_locked(DnRecord)
    # Modify the password
    Rec = [(ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass),
           (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60)))];
@@ -692,7 +692,45 @@ def HandleChPass(Reply,DnRecord,Key):
    l.modify_s(Dn,Rec);
 
    return Reply;
-      
+
+def HandleChKrbPass(Reply,DnRecord,Key):
+   # Connect to the ldap server, will throw an exception if account locked.
+   l = connect_to_ldap_and_check_if_locked(DnRecord)
+
+   user = GetAttr(DnRecord,"uid")
+   krb_proc = subprocess.Popen( ('ud-krb-reset', user), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+   krb_proc.stdin.close()
+   out = krb_proc.stdout.readlines()
+   krb_proc.wait()
+   exitcode = krb_proc.returncode
+
+   # Use GPG to encrypt it
+   m = "Tried to reset your kerberos principal's password.\n"
+   if exitcode == 0:
+      m += "The exitcode of the reset script was zero, indicating that everything\n"
+      m += "worked.  However, this being software who knows.  Script's output below."
+   else:
+      m += "The exitcode of the reset script was %d, indicating that something\n"%(exitcode)
+      m += "went terribly, terribly wrong.  Please consult the script's output below\n"
+      m += "for more information.  Contact the admins if you have any questions or\n"
+      m += "require assitance."
+
+   m += "\n"+''.join( map(lambda x: "| "+x, out)  )
+
+   Message = GPGEncrypt(m, "0x"+Key[1],Key[4]);
+   if Message == None:
+      raise UDFormatError, "Unable to generate the encrypted reply, gpg failed.";
+
+   Subst = {};
+   Subst["__FROM__"] = ChPassFrom;
+   Subst["__EMAIL__"] = EmailAddress(DnRecord);
+   Subst["__CRYPTTYPE__"] = get_crypttype_preamble(Key)
+   Subst["__PASSWORD__"] = Message;
+   Subst["__ADMIN__"] = ReplyTo;
+   Reply = Reply + TemplateSubst(Subst,open(TemplatesDir+"passwd-changed","r").read());
+
+   return Reply;
+
 # Start of main program
 
 # Drop messages from a mailer daemon.
@@ -790,9 +828,12 @@ try:
    if sys.argv[1] == "ping":
       Reply = HandlePing(Reply,Attrs[0],pgp.key_info);
    elif sys.argv[1] == "chpass":
-      if PlainText.strip().find("Please change my Debian password") != 0:
-         raise UDFormatError,"Please send a signed message where the first line of text is the string 'Please change my Debian password'";
-      Reply = HandleChPass(Reply,Attrs[0],pgp.key_info);
+      if PlainText.strip().find("Please change my Debian password") >= 0:
+         Reply = HandleChPass(Reply,Attrs[0],pgp.key_info);
+      elif PlainText.strip().find("Please change my Kerberos password") >= 0:
+         Reply = HandleChKrbPass(Reply,Attrs[0],pgp.key_info);
+      else:
+         raise UDFormatError,"Please send a signed message where the first line of text is the string 'Please change my Debian password' or some other string we accept here.";
    elif sys.argv[1] == "change":
       Reply = HandleChange(Reply,Attrs[0],pgp.key_info);
    else: