Немного о безопасности

Вадим Годунко <vgodunko@gmail.com>, 2008 г.

Фактически всегда в распределённых информационных системах остро стоит вопрос обеспечения информационной безопасности. Спецификация CORBA так же не обходит стороной эти вопросы и предлагает ряд стандартных механизмов обеспечения информационной безопасности. PolyORB поддерживает то подмножество механизмов, которое не требует специальных дополнительных коммерческих библиотек.

Важной особенностью службы безопасности CORBA является её полная прозрачность для приложений. Всё, что требуется со стороны разработчика — подключить библиотеки службы безопасности к программе и соответствующим образом настроить PolyORB.

Прежде чем переходить к описанию способов подключения и настроек PolyORB хочется немного объяснить основные принципы работы службы безопасности CORBA.

Настройка параметров службы безопасности — политики безопасности — осуществляется отдельно для сервера и клиента. Для сервера задаются минимальные — требуемые — значения политики безопасности, и определяются максимальные — поддерживаемые — значения политики безопасности. Для клиента задаются только минимальные — требуемые — значения политики безопасности. Брокеры объектных запросов клиента и сервера самостоятельно согласуют свои пожелания и возможности таким образом, что бы имеющимися средствами удовлетворить минимальные — требуемые — как клиентом, так и сервером, значения политики безопасности.

Оценка требуемых и поддерживаемых значений политики безопасности выполняется по следующим критериям:

Параметр поддерживается сервером требуется сервером требуется клиентом
Integrity Сервер поддерживает контроль целостности передаваемых/принимаемых данных Сервер требует наличия контроля передаваемых/принимаемых данных Клиент требует наличие контроля принимаемых/передаваемых данных
Confidentiality Сервер поддерживает шифрование передаваемых/принимаемых данных Сервер требует использования шифрования при передаче/приёме данных Клиент требует шифрования при передаче/приёме данных
EstablishTrustInTarget Сервер способен аутентифицировать себя для клиента Клиент требует аутентифицировать сервер
EstablishTrustInClient Сервер способен выполнять аутентификацию клиентов Сервер требует проведение аутентификации клиента
IdentityAssertion Сервер способен воспринимать подтверждение клиентом делегирования

Все приведённые критерии поддерживаются PolyORB, однако здесь опущены неподдерживаемые критерии — DelegationByClient (требует библиотек поддержки сертификатор полномочий X.509AC), DetectReplay и DetectMisordering (требует наличия библиотек поддержки специальных протоколов передачи защищаемых данных).

Для реализации безопасного взаимодействия возможно использование одного или нескольких механизмов защиты данных. Каждый механизм способен реализовывать свой специфический набор критериев, и зачастую очень специфичным образом.

Имеется возможность группировки некоторых механизмов безопасного взаимодействия и одновременного их использования. Это возможно благодаря наличию трёх уровней безопасного взаимодействия: транспортного уровня, уровеня дополнительной аутентификации и уровня атрибутов безопасности. На каждом уровне возможно выбрать максимиум только один из поддерживаемых механизмов.

PolyORB предоставляет следующие механизмы безопасного взаимодействия:

Механизм Уровень Потенциально поддерживаемые критерии
SSL/TLS транспортный Integrity, Confidentiality, EstablishTrustInTarget, EstablishTrustInClient
GSSUP дополнительная аутентификация EstablishTrustInClient
Forward Trust Evaluation атрибуты безопасности IdentityAssertion

Точная и аккуратная настройка всех механизмов безопасности требует детального понимания всего многообразия имеющихся параметров и поэтому выходит за рамки данного описания. В качестве примера мы будем использовать самый простой изх имеющихся механизмов — механизм аутентификации по имени и паролю — GSSUP. Этот механизм выполняет передачу пароля в открытом виде, и, таким образом, его можно безопасно использовать только одновременно с использованием шифрования данных на транспортном уровне.

Теперь пришло время доработать клиентскую и серверную программы. Исправления в клиентской программе сводятся к подключению в программу пакета PolyORB.Setup.Secure_Client. Далее приводится полный текст клиента для удобства использования:

with Ada.Characters.Conversions;
with Ada.Command_Line;
with Ada.Integer_Wide_Text_IO;
with Ada.Strings.Wide_Unbounded.Wide_Text_IO;
with Ada.Wide_Text_IO;

with CORBA.ORB;

with PolyORB.Setup.Client;
--  Конфигурирование PolyORB для работы в режиме клиента.
--
with PolyORB.Setup.Secure_Client;

with Users.User;
with Users.UserFactory; -- .Helper;
with Users.UserFinder; -- .Helper;

procedure Client is
begin
   --  Инициализация ORB.

   declare
      Argv : CORBA.ORB.Arg_List := CORBA.ORB.Command_Line_Arguments;

   begin
      CORBA.ORB.Init (CORBA.ORB.To_CORBA_String ("ORB"), Argv);
   end;

   if Ada.Command_Line.Argument_Count /= 2 then
      Ada.Wide_Text_IO.Put_Line ("client <factory_ior> <finder_ior>");

      return;
   end if;

   declare
      Factory : Users.UserFactory.Ref;
      Finder  : Users.UserFinder.Ref;
      Current : Users.User.Ref;

      procedure Create_New_User is
         Name    : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
         Surname : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;

      begin
         --  Ввод имени и фамилии пользователя.

         Ada.Wide_Text_IO.Put ("Name: ");
         Ada.Wide_Text_IO.Flush;
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (Name);
         Ada.Wide_Text_IO.Put ("Surname: ");
         Ada.Wide_Text_IO.Flush;
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (Surname);

         --  Создание нового объекта.

         Current :=
           Users.UserFactory.create
            (Factory,
             CORBA.To_CORBA_Wide_String
              (Ada.Strings.Wide_Unbounded.To_Wide_String (Name)),
             CORBA.To_CORBA_Wide_String
              (Ada.Strings.Wide_Unbounded.To_Wide_String (Surname)));
      end Create_New_User;

      procedure Save_IOR is
         File_Name : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
         File      : Ada.Wide_Text_IO.File_Type;

      begin
         --  Проверка наличия текущей объектной ссылки.

         if Users.User.Is_Nil (Current) then
            Ada.Wide_Text_IO.Put_Line ("ERROR: No IOR in buffer");
         end if;

         --  Ввод имени файла.

         Ada.Wide_Text_IO.Put ("File name: ");
         Ada.Wide_Text_IO.Flush;
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (File_Name);

         --  Создание файла и сохранение в нём объектной ссылки.

         Ada.Wide_Text_IO.Create
          (File,
           Ada.Wide_Text_IO.Out_File,
           Ada.Characters.Conversions.To_String
            (Ada.Strings.Wide_Unbounded.To_Wide_String (File_Name)));
         Ada.Wide_Text_IO.Put_Line
          (File,
           Ada.Characters.Conversions.To_Wide_String
            (CORBA.To_Standard_String
              (CORBA.ORB.Object_To_String (Current))));
         Ada.Wide_Text_IO.Close (File);
      end Save_IOR;

      procedure Load_IOR is
         File_Name : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
         File      : Ada.Wide_Text_IO.File_Type;
         Image     : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;

      begin
         --  Ввод имени файла.

         Ada.Wide_Text_IO.Put ("File name: ");
         Ada.Wide_Text_IO.Flush;
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (File_Name);

         --  Открытие файла и загрузка из него объектной ссылки.

         Ada.Wide_Text_IO.Open
          (File,
           Ada.Wide_Text_IO.In_File,
           Ada.Characters.Conversions.To_String
            (Ada.Strings.Wide_Unbounded.To_Wide_String (File_Name)));
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (File, Image);
         Ada.Wide_Text_IO.Close (File);

         --  Создание объектной ссылки по её образу.

         CORBA.ORB.String_To_Object
          (CORBA.To_CORBA_String
            (Ada.Characters.Conversions.To_String
              (Ada.Strings.Wide_Unbounded.To_Wide_String (Image))),
           Current);
      end Load_IOR;

      procedure Show_Attributes is
      begin
         --  Проверка наличия текущей объектной ссылки.

         if Users.User.Is_Nil (Current) then
            Ada.Wide_Text_IO.Put_Line ("ERROR: No IOR in buffer");

            return;
         end if;

         --  Получение и отображение значений атрибутов.

         Ada.Wide_Text_IO.Put_Line
          ("Name: '"
             & CORBA.To_Standard_Wide_String (Users.User.Get_name (Current))
             & "'");
         Ada.Wide_Text_IO.Put_Line
          ("Surname: '"
             & CORBA.To_Standard_Wide_String (Users.User.Get_surname (Current))
             & "'");
      end Show_Attributes;

      procedure Search_By_Name is
         Name  : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
         Found : Users.UserSequence;
         Index : Integer;

      begin
         Ada.Wide_Text_IO.Put ("Name: ");
         Ada.Wide_Text_IO.Flush;
         Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (Name);

         Found :=
           Users.UserFinder.by_name
            (Finder,
             CORBA.To_CORBA_Wide_String
              (Ada.Strings.Wide_Unbounded.To_Wide_String (Name)));

         Ada.Wide_Text_IO.Put_Line
          ("Found" & Integer'Wide_Image (Users.Length (Found)));  

         for J in 1 .. Users.Length (Found) loop
            Ada.Wide_Text_IO.Put_Line
             (Integer'Wide_Image (J)
                & ": '"
                & CORBA.To_Standard_Wide_String
                   (Users.User.Get_name
                     (Users.User.Convert_Forward.To_Ref
                       (Users.Get_Element (Found, J))))
                & "', '"
                & CORBA.To_Standard_Wide_String
                   (Users.User.Get_surname
                     (Users.User.Convert_Forward.To_Ref
                       (Users.Get_Element (Found, J))))
                & "'");
         end loop;

         Ada.Wide_Text_IO.Put ("Selection (0 - ignore): ");
         Ada.Wide_Text_IO.Flush;
         Ada.Integer_Wide_Text_IO.Get (Index);
         Ada.Wide_Text_IO.Skip_Line;

         if Index in 1 .. Users.Length (Found) then
            Current :=
              Users.User.Convert_Forward.To_Ref
               (Users.Get_Element (Found, Index));
         end if;
      end Search_By_Name;

   begin
      --  Преобразование параметров командной строки в объектные ссылки.

      CORBA.ORB.String_To_Object
       (CORBA.To_CORBA_String (Ada.Command_Line.Argument (1)), Factory);

      CORBA.ORB.String_To_Object
       (CORBA.To_CORBA_String (Ada.Command_Line.Argument (2)), Finder);

      --  Цикл обработки ввода пользователя.

      loop
         declare
            Choice : Natural;

         begin
            Ada.Wide_Text_IO.New_Line;
            Ada.Wide_Text_IO.Put_Line ("Menu:");
            Ada.Wide_Text_IO.Put_Line (" 1: Create new user");
            Ada.Wide_Text_IO.Put_Line (" 2: Save IOR");
            Ada.Wide_Text_IO.Put_Line (" 3: Load IOR");
            Ada.Wide_Text_IO.Put_Line (" 4: Show attributes");
            Ada.Wide_Text_IO.Put_Line (" 5: Search by name");
            Ada.Wide_Text_IO.New_Line;
            Ada.Wide_Text_IO.Put_Line (" 0: Exit");
            Ada.Wide_Text_IO.New_Line;

            Ada.Wide_Text_IO.Put ("Choice: ");
            Ada.Wide_Text_IO.Flush;
            Ada.Integer_Wide_Text_IO.Get (Choice);
            Ada.Wide_Text_IO.Skip_Line;

            case Choice is
               when 0 =>
                  exit;

               when 1 =>
                  Create_New_User;

               when 2 =>
                  Save_IOR;

               when 3 =>
                  Load_IOR;

               when 4 =>
                  Show_Attributes;

               when 5 =>
                  Search_By_Name;

               when others =>
                  null;
            end case;

            --  Вывод объектной ссылки нового пользователя.
           
            if not Users.User.Is_Nil (Current) then
               Ada.Wide_Text_IO.Put_Line
                ("Current user's IOR: '"
                   & Ada.Characters.Conversions.To_Wide_String
                      (CORBA.To_Standard_String
                        (CORBA.ORB.Object_To_String (Current)))
                   & "'");
            end if;
         end;
      end loop;
   end;
end Client;

Для настройки клиента необходимо задать ряд параметров в конфигурационном файле PolyORB. В приведённом ниже конфигурационном файле настраивается возможность использования механизма аутентификации GSSUP и необходимые параметры (имя пользователя, пароль, домен):

##  Управление безопасностью клиента.

[security_manager]
own_credentials=user_credentials

[user_credentials]
authentication_credentials_type=gssup
gssup.username=user
gssup.password=pass
gssup.target_name=@ada-ru.org

##  Управление точками доступа.

[access_points]
srp=disable
soap=disable
iiop=enable
iiop.ssliop=disable
diop=disable
uipmc=disable

## Управление поддерживаемыми протоколами.

[modules]
binding_data.srp=disable
binding_data.soap=disable
binding_data.iiop=enable
binding_data.iiop.ssliop=disable
binding_data.diop=disable
binding_data.uipmc=disable

Необходимо отметить, что механизм аутентификации клиента GSSUP может быть затребован только сервером, но не может требоваться клиентом.

Для включения безопасного взаимодействия на сервере требуется сделать почти тоже самое. Первым делом необходимо подключить пакет PolyORB.Setup.Secure_Server к программе сервера. При использовании механизмов безопасного взаимодействия рекомендуется также использовать одну из конфигураций PolyORB, поддерживающую многозадачную обработку запросов. Текст программы сервера будет выглядеть следующим образом:

with Ada.Text_IO;

with CORBA.Object;
with CORBA.ORB;
with CORBA.Policy;
with PortableServer.IdAssignmentPolicy;
with PortableServer.ImplicitActivationPolicy;
with PortableServer.LifespanPolicy;
with PortableServer.POA.Helper;
with PortableServer.POAManager;
with PortableServer.RequestProcessingPolicy;
with PortableServer.ServantManager;

--with PolyORB.Setup.No_Tasking_Server;
with PolyORB.Setup.Thread_Pool_Server;
--  Конфигурирование PolyORB для работы в режиме многозадачного
--  сервера. Это необходимо при для корректной работы механизмов
--  защищенного взаимодействия.

with PolyORB.Setup.Secure_Server;
--  Конфигурирование PolyORB для работы в режиме защищенного сервера.

with Activator.Impl;
with Globals;
with Users.UserFactory.Impl;
with Users.UserFinder.Impl;

procedure Server is
begin
   --  Инициализация ORB.

   declare
      Argv : CORBA.ORB.Arg_List := CORBA.ORB.Command_Line_Arguments;

   begin
      CORBA.ORB.Init (CORBA.ORB.To_CORBA_String ("ORB"), Argv);
   end;

   declare
      Root_POA : PortableServer.POA.Local_Ref;

   begin
      --  Получение ссылки на корневой объектный адаптер.

      Root_POA :=
        PortableServer.POA.Helper.To_Local_Ref
         (CORBA.ORB.Resolve_Initial_References
           (CORBA.ORB.To_CORBA_String ("RootPOA")));

      --  Активация корневого объектного адаптера.

      PortableServer.POAManager.Activate
       (PortableServer.POA.Get_The_POAManager (Root_POA));

      --  Создание объектного адаптера для объектов пользователей.

      declare
         Policies      : CORBA.Policy.PolicyList;
         Lifespan      : PortableServer.LifespanPolicy.Ref
           := PortableServer.POA.Create_Lifespan_Policy
               (PortableServer.PERSISTENT);
         Id_Assignment : PortableServer.IdAssignmentPolicy.Ref
           := PortableServer.POA.Create_Id_Assignment_Policy
               (PortableServer.USER_ID);
         Activation    : PortableServer.ImplicitActivationPolicy.Ref
           := PortableServer.POA.Create_Implicit_Activation_Policy
               (PortableServer.NO_IMPLICIT_ACTIVATION);
         Processing    : PortableServer.RequestProcessingPolicy.Ref
           := PortableServer.POA.Create_Request_Processing_Policy
               (PortableServer.USE_SERVANT_MANAGER);

      begin
         CORBA.Policy.IDL_SEQUENCE_Policy.Append
          (Policies, CORBA.Policy.Ref (Lifespan));
         CORBA.Policy.IDL_SEQUENCE_Policy.Append
          (Policies, CORBA.Policy.Ref (Id_Assignment));
         CORBA.Policy.IDL_SEQUENCE_Policy.Append
          (Policies, CORBA.Policy.Ref (Activation));
         CORBA.Policy.IDL_SEQUENCE_Policy.Append
          (Policies, CORBA.Policy.Ref (Processing));

         Globals.User_POA :=
           PortableServer.POA.Local_Ref
            (PortableServer.POA.Create_POA
              (Root_POA,
               CORBA.To_CORBA_String ("UserPOA"),
               PortableServer.POA.Get_The_POAManager (Root_POA),
               Policies)); 

         --  Создание активатора и его регистрация в объектном адаптере.

         declare
            Ref : Activator.Local_Ref;

         begin
            Activator.Set (Ref, new Activator.Impl.Object);  
            PortableServer.POA.Set_Servant_Manager (Globals.User_POA, Ref);
         end;
      end;

      --  Создание объекта-фабрики и объектной ссылки на этот объект.

      declare
         Ref : CORBA.Object.Ref;

      begin
         Ref :=
           PortableServer.POA.Servant_To_Reference
            (Root_POA,
             new Users.UserFactory.Impl.Object);

         --  Вывод на экран сформированной объектной ссылки.

         Ada.Text_IO.Put_Line
           ("'"
            & CORBA.To_Standard_String (CORBA.Object.Object_To_String (Ref))
            & "'");
      end;

      --  Создание объекта-поисковика и объектной ссылки на этот объект.

      declare
         Ref : CORBA.Object.Ref;

      begin
         Ref :=
           PortableServer.POA.Servant_To_Reference
            (Root_POA,
             new Users.UserFinder.Impl.Object);

         --  Вывод на экран сформированной объектной ссылки.

         Ada.Text_IO.Put_Line
           ("'"
            & CORBA.To_Standard_String (CORBA.Object.Object_To_String (Ref))
            & "'");
      end;
   end;

   --  Передача нити главной подпрограммы в ведение ORB.

   CORBA.ORB.Run;
end Server;

Настройка механизмов безопасного взаимодействия осуществляется в конфигурационном файле отдельно для каждого объектного адаптера. В нашем случае включается механизм аутентификации клиента GSSUP и требуется обязательное выполнение аутентификации:

##  Управление безопасностью сервера.

[UserPOA]
authentication_mechanism=user_gssup
authentication_required=true

[user_gssup]
mechanism=gssup
gssup.target_name=@ada-ru.org
gssup.passwd_file=passwd.pwd

##  Фиксация точки доступа.
[iiop]
polyorb.protocols.iiop.default_addr=127.0.0.1
polyorb.protocols.iiop.default_port=6543

##  Управление точками доступа.

[access_points]
srp=disable
soap=disable
iiop=enable
iiop.ssliop=disable
diop=disable
uipmc=disable

## Управление поддерживаемыми протоколами.

[modules]
binding_data.srp=disable
binding_data.soap=disable
binding_data.iiop=enable
binding_data.iiop.ssliop=disable
binding_data.diop=disable
binding_data.uipmc=disable

И последнее, что осталось сделать — создать файл пользователей и паролей — passwd.pwd:

user:pass

Собрав модифицированные версии клиента и сервера, запустив их можно быть уверенным, что никто не сможет пробраться на сервер не аутентифицировав себя. В этом несложно убедиться поигравшись с настройками конфигурационных файлов, например, задав неверную пару пользователь/пароль.

Особо пытливые читатели могут проанализировать объектные ссылки объектов‐пользователей. Они смогут увидеть, что в профиле появился новый тэговый компонент, описывающий механизмы защищенного взаимодействия и требования по безопасному взаимодействия со стороны сервера. К сожалению на момент написания поддержка детальной расшифровки тэга конфигурации защищенного взаимодействия ещё не была включена в программу po_catref.