------------------------------------------------------------------------------
--                                                                          --
--                           POLYORB COMPONENTS                             --
--                                                                          --
--         POLYORB.SECURITY.AUTHENTICATION_MECHANISMS.GSSUP_TARGET          --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--         Copyright (C) 2005-2006, Free Software Foundation, Inc.          --
--                                                                          --
-- PolyORB is free software; you  can  redistribute  it and/or modify it    --
-- under terms of the  GNU General Public License as published by the  Free --
-- Software Foundation;  either version 2,  or (at your option)  any  later --
-- version. PolyORB is distributed  in the hope that it will be  useful,    --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License  for more details.  You should have received  a copy of the GNU  --
-- General Public License distributed with PolyORB; see file COPYING. If    --
-- not, write to the Free Software Foundation, 51 Franklin Street, Fifth    --
-- Floor, Boston, MA 02111-1301, USA.                                       --
--                                                                          --
--                                                                          --
--                  PolyORB is maintained by AdaCore                        --
--                     (email: sales@adacore.com)                           --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Strings.Fixed;
with Ada.Text_IO;

with PolyORB.Buffers;
with PolyORB.Errors;
with PolyORB.Initialization;
with PolyORB.Parameters;
with PolyORB.Representations.CDR.Common;
with PolyORB.Security.Identities.Principal_Name;
with PolyORB.Security.Exported_Names.GSSUP;
with PolyORB.Utils.Strings;

package body PolyORB.Security.Authentication_Mechanisms.GSSUP_Target is

   use Ada.Streams;
   use PolyORB.Buffers;
   use PolyORB.Representations.CDR.Common;
   use PolyORB.Security.Exported_Names;
   use PolyORB.Security.Exported_Names.GSSUP;

   procedure Initialize;

   function Create_Mechanism
     (Section_Name : String)
      return Target_Authentication_Mechanism_Access;

   type Error_Code is new PolyORB.Types.Unsigned_Long;

   GSS_UP_S_G_UNSPECIFIED  : constant Error_Code := 1;
   GSS_UP_S_G_NOUSER       : constant Error_Code := 2;
   GSS_UP_S_G_BAD_PASSWORD : constant Error_Code := 3;
   GSS_UP_S_G_BAD_TARGET   : constant Error_Code := 4;

   Encoded_Mechanism_OID : PolyORB.Security.Types.Stream_Element_Array_Access;

   -----------------------------
   -- Accept_Security_Context --
   -----------------------------

   procedure Accept_Security_Context
     (Mechanism    : access GSSUP_Target_Authentication_Mechanism;
      Token        :        PolyORB.Security.Types.Stream_Element_Array_Access;
      Success      : out    Boolean;
      Return_Token : out
        PolyORB.Security.Types.Stream_Element_Array_Access;
      Identity     : out    PolyORB.Security.Identities.Identity_Access)
   is
      use PolyORB.Security.Types;
      use PolyORB.Types;

      procedure Create_Error_Token (Code : Error_Code);

      ------------------------
      -- Create_Error_Token --
      ------------------------

      procedure Create_Error_Token (Code : Error_Code) is
         Buffer : Buffer_Access := new Buffer_Type;

      begin
         Start_Encapsulation (Buffer);

         Marshall (Buffer, Unsigned_Long (Code));

         Return_Token := new Stream_Element_Array'(Encapsulate (Buffer));

         Release (Buffer);
      end Create_Error_Token;

      User_Name   : PolyORB.Types.String;
      Password    : PolyORB.Types.String;
      Target_Name : Exported_Name_Access;
      Buffer      : aliased Buffer_Type;
      First       : Stream_Element_Offset;
      Last        : Stream_Element_Offset;

   begin
      --  Extract mechanism OID

      if Token (Token'First + 1) < 128 then
         First := Token'First + 2;

      else
         raise Program_Error;
      end if;

      if Token (First + 1) < 128 then
         Last := First + Stream_Element_Offset (Token (First + 1)) + 2 - 1;

      else
         raise Program_Error;
      end if;

      --  Check is mechanism OID is valid

      if Token (First .. Last) /= Encoded_Mechanism_OID.all then
         Success := False;
         Create_Error_Token (GSS_UP_S_G_UNSPECIFIED);

         return;
      end if;

      --  Unmarshall Initial Token

      declare
         Data : aliased Encapsulation := Token (Last + 1 .. Token'Last);

      begin
         Decapsulate (Data'Access, Buffer'Access);

         User_Name := Unmarshall_Latin_1_String (Buffer'Access);
         Password  := Unmarshall_Latin_1_String (Buffer'Access);

         declare
            use PolyORB.Errors;

            Encoded_Name : constant Stream_Element_Array
              := Unmarshall (Buffer'Access);
            Error        : Error_Container;

         begin
            Decode (Encoded_Name, Target_Name, Error);

            if Found (Error) then
               raise Program_Error;
            end if;
         end;
      end;

      --  Check Target Name

      if not Is_Equivalent (Target_Name, Mechanism.Target_Name) then
         Success := False;
         Create_Error_Token (GSS_UP_S_G_BAD_TARGET);
         Release_Contents (Target_Name);

         return;
      end if;

      Release_Contents (Target_Name);

      --  Check password

      declare
         use Ada.Strings.Fixed;
         use Ada.Text_IO;
         use PolyORB.Security.Identities.Principal_Name;

         File      : File_Type;
         Buffer    : String (1 .. 1024);
         Last      : Natural;
         Delimiter : Natural;

      begin
         Open (File, In_File, To_Standard_String (Mechanism.Passwd_File));

         while not End_Of_File (File) loop
            Get_Line (File, Buffer, Last);

            Delimiter := Index (Buffer (1 .. Last), ":");

            if Delimiter /= 0 then
               if Buffer (1 .. Delimiter - 1) = User_Name then
                  if Buffer (Delimiter + 1 .. Last) = Password then
                     Success := True;
                     Close (File);

                     Identity :=
                       Create_Principal_Name_Identity
                       (Create_GSSUP_Exported_Name
                        (To_Standard_String (User_Name)));

                     return;

                  else
                     Success := False;
                     Create_Error_Token (GSS_UP_S_G_BAD_PASSWORD);
                     Close (File);

                     return;
                  end if;
               end if;
            end if;
         end loop;

         Close (File);
      end;

      Success := False;
      Create_Error_Token (GSS_UP_S_G_NOUSER);
   end Accept_Security_Context;

   ----------------------
   -- Create_Mechanism --
   ----------------------

   function Create_Mechanism
     (Section_Name : String)
      return Target_Authentication_Mechanism_Access
   is
      use PolyORB.Parameters;
      use PolyORB.Types;

      Target_Name : constant String
        := Get_Conf (Section_Name, "gssup.target_name", "");
      Passwd_File : constant String
        := Get_Conf (Section_Name, "gssup.passwd_file", "");

   begin
      if Target_Name = ""
        or else Passwd_File = ""
      then
         raise Program_Error;
      end if;

      return
        new GSSUP_Target_Authentication_Mechanism'
        (Mechanism_OID     =>
           PolyORB.ASN1.To_Object_Identifier
           (PolyORB.Security.Types.GSSUPMechOID),
         Target_Name       => Create_GSSUP_Exported_Name (Target_Name),
         Identity_Types    => PolyORB.Security.Types.ITT_Principal_Name,
         Naming_Mechanisms =>
           PolyORB.Security.Types.OID_Lists."+"
           (PolyORB.ASN1.To_Object_Identifier
            (PolyORB.Security.Types.GSSUPMechOID)),
         Passwd_File       => To_PolyORB_String (Passwd_File));
   end Create_Mechanism;

   ----------------
   -- Initialize --
   ----------------

   procedure Initialize is
   begin
      Encoded_Mechanism_OID :=
        new Ada.Streams.Stream_Element_Array'
        (PolyORB.ASN1.Encode
         (PolyORB.ASN1.To_Object_Identifier
          (PolyORB.Security.Types.GSSUPMechOID)));

      Register ("gssup", Create_Mechanism'Access);
   end Initialize;

begin
   declare
      use PolyORB.Initialization;
      use PolyORB.Utils.Strings;

   begin
      Register_Module
        (Module_Info'
         (Name      =>
          +"polyorb.security.authentication_mechanisms.gssup_target",
          Conflicts => PolyORB.Initialization.String_Lists.Empty,
          Depends   => PolyORB.Initialization.String_Lists.Empty,
          Provides  => PolyORB.Initialization.String_Lists.Empty,
          Implicit  => False,
          Init      => Initialize'Access,
          Shutdown  => null));
   end;
end PolyORB.Security.Authentication_Mechanisms.GSSUP_Target;
