Thread: krb_match_realm

krb_match_realm

From
Magnus Hagander
Date:
Attached patch implements krb_match_realm for krb5, gssapi and sspi per
complaint from Henry. Comments welcome.

Working on documentation which will of course be ready when it's
committed :)

Oh, and it changes the krb username handling to be the same as the
gssapi one. I've never heard of anybody actually using the other version
that it used to support, and the comment clearly states that it was
broken for the really complex scenarios anyway - something nobody has
complained about.

//Magnus
Index: backend/libpq/auth.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/auth.c,v
retrieving revision 1.156
diff -c -r1.156 auth.c
*** backend/libpq/auth.c    14 Sep 2007 15:58:02 -0000    1.156
--- backend/libpq/auth.c    1 Nov 2007 11:28:54 -0000
***************
*** 42,47 ****
--- 42,48 ----
  char       *pg_krb_srvnam;
  bool        pg_krb_caseins_users;
  char       *pg_krb_server_hostname = NULL;
+ char       *pg_krb_match_realm = NULL;

  #ifdef USE_PAM
  #ifdef HAVE_PAM_PAM_APPL_H
***************
*** 103,132 ****
  #endif

  /*
-  * pg_an_to_ln -- return the local name corresponding to an authentication
-  *                  name
-  *
-  * XXX Assumes that the first aname component is the user name.  This is NOT
-  *       necessarily so, since an aname can actually be something out of your
-  *       worst X.400 nightmare, like
-  *          ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
-  *       Note that the MIT an_to_ln code does the same thing if you don't
-  *       provide an aname mapping database...it may be a better idea to use
-  *       krb5_an_to_ln, except that it punts if multiple components are found,
-  *       and we can't afford to punt.
-  */
- static char *
- pg_an_to_ln(char *aname)
- {
-     char       *p;
-
-     if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
-         *p = '\0';
-     return aname;
- }
-
-
- /*
   * Various krb5 state which is not connection specfic, and a flag to
   * indicate whether we have initialised it yet.
   */
--- 104,109 ----
***************
*** 216,221 ****
--- 193,199 ----
      krb5_auth_context auth_context = NULL;
      krb5_ticket *ticket;
      char       *kusername;
+     char       *cp;

      if (get_role_line(port->user_name) == NULL)
          return STATUS_ERROR;
***************
*** 240,247 ****
       * The "client" structure comes out of the ticket and is therefore
       * authenticated.  Use it to check the username obtained from the
       * postmaster startup packet.
-      *
-      * I have no idea why this is considered necessary.
       */
  #if defined(HAVE_KRB5_TICKET_ENC_PART2)
      retval = krb5_unparse_name(pg_krb5_context,
--- 218,223 ----
***************
*** 263,269 ****
          return STATUS_ERROR;
      }

!     kusername = pg_an_to_ln(kusername);
      if (pg_krb_caseins_users)
          ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
      else
--- 239,280 ----
          return STATUS_ERROR;
      }

!     cp = strchr(kusername, '@');
!     if (cp)
!     {
!         *cp = '\0';
!         cp++;
!
!         if (pg_krb_match_realm != NULL && strlen(pg_krb_match_realm))
!         {
!             /* Match realm against configured */
!             if (pg_krb_caseins_users)
!                 ret = pg_strcasecmp(pg_krb_match_realm, cp);
!             else
!                 ret = strcmp(pg_krb_match_realm, cp);
!
!             if (ret)
!             {
!                 elog(DEBUG2,
!                      "krb5 realm (%s) and configured realm (%s) don't match",
!                      cp, pg_krb_match_realm);
!
!                 krb5_free_ticket(pg_krb5_context, ticket);
!                 krb5_auth_con_free(pg_krb5_context, auth_context);
!                 return STATUS_ERROR;
!             }
!         }
!     }
!     else if (pg_krb_match_realm && strlen(pg_krb_match_realm))
!     {
!         elog(DEBUG2,
!              "krb5 did not return realm but realm matching was requested");
!
!         krb5_free_ticket(pg_krb5_context, ticket);
!         krb5_auth_con_free(pg_krb5_context, auth_context);
!         return STATUS_ERROR;
!     }
!
      if (pg_krb_caseins_users)
          ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
      else
***************
*** 509,522 ****
                       maj_stat, min_stat);

      /*
!      * Compare the part of the username that comes before the @
!      * sign only (ignore realm). The GSSAPI libraries won't have
!      * authenticated the user if he's from an invalid realm.
       */
      if (strchr(gbuf.value, '@'))
      {
          char *cp = strchr(gbuf.value, '@');
          *cp = '\0';
      }

      if (pg_krb_caseins_users)
--- 520,561 ----
                       maj_stat, min_stat);

      /*
!      * Split the username at the realm separator
       */
      if (strchr(gbuf.value, '@'))
      {
          char *cp = strchr(gbuf.value, '@');
          *cp = '\0';
+         cp++;
+
+         if (pg_krb_match_realm != NULL && strlen(pg_krb_match_realm))
+         {
+             /*
+              * Match the realm part of the name first
+              */
+             if (pg_krb_caseins_users)
+                 ret = pg_strcasecmp(pg_krb_match_realm, cp);
+             else
+                 ret = strcmp(pg_krb_match_realm, cp);
+
+             if (ret)
+             {
+                 /* GSS realm does not match */
+                 elog(DEBUG2,
+                      "GSSAPI realm (%s) and configured realm (%s) don't match",
+                      cp, pg_krb_match_realm);
+                 gss_release_buffer(&lmin_s, &gbuf);
+                 return STATUS_ERROR;
+             }
+         }
+     }
+     else if (pg_krb_match_realm && strlen(pg_krb_match_realm))
+     {
+         elog(DEBUG2,
+              "GSSAPI did not return realm but realm matching was requested");
+
+         gss_release_buffer(&lmin_s, &gbuf);
+         return STATUS_ERROR;
      }

      if (pg_krb_caseins_users)
***************
*** 792,797 ****
--- 831,851 ----

      free(tokenuser);

+     /*
+      * Compare realm/domain if requested. In SSPI, always compare case insensitive.
+      */
+     if (pg_krb_match_realm && strlen(pg_krb_match_realm))
+     {
+         if (pg_strcasecmp(pg_krb_match_realm, domainname))
+         {
+             elog(DEBUG2,
+                  "SSPI domain (%s) does and configured domain (%s) don't match",
+                  domainname, pg_krb_match_realm);
+
+             return STATUS_ERROR;
+         }
+     }
+
      /*
       * We have the username (without domain/realm) in accountname, compare
       * to the supplied value. In SSPI, always compare case insensitive.
Index: backend/utils/misc/guc.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.423
diff -c -r1.423 guc.c
*** backend/utils/misc/guc.c    26 Sep 2007 22:36:30 -0000    1.423
--- backend/utils/misc/guc.c    1 Nov 2007 11:28:54 -0000
***************
*** 2044,2049 ****
--- 2044,2059 ----
      },

      {
+         {"krb_match_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+             gettext_noop("Sets realm to match Kerberos and GSSAPI users against."),
+             NULL,
+             GUC_SUPERUSER_ONLY
+         },
+         &pg_krb_match_realm,
+         NULL, NULL, NULL
+     },
+
+     {
          {"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY,
              gettext_noop("Sets the location of the Kerberos server key file."),
              NULL,
Index: backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.231
diff -c -r1.231 postgresql.conf.sample
*** backend/utils/misc/postgresql.conf.sample    26 Sep 2007 22:36:30 -0000    1.231
--- backend/utils/misc/postgresql.conf.sample    1 Nov 2007 11:28:54 -0000
***************
*** 85,90 ****
--- 85,91 ----
  #krb_server_hostname = ''        # empty string matches any keytab entry
                      # (change requires restart, kerberos only)
  #krb_caseins_users = off        # (change requires restart)
+ #krb_match_realm = ''           # (change requires restart)

  # - TCP Keepalives -
  # see 'man 7 tcp' for details
Index: include/libpq/auth.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/libpq/auth.h,v
retrieving revision 1.33
diff -c -r1.33 auth.h
*** include/libpq/auth.h    5 Jan 2007 22:19:55 -0000    1.33
--- include/libpq/auth.h    1 Nov 2007 11:28:54 -0000
***************
*** 20,25 ****
--- 20,26 ----
  extern char *pg_krb_srvnam;
  extern bool pg_krb_caseins_users;
  extern char *pg_krb_server_hostname;
+ extern char *pg_krb_match_realm;

  extern void ClientAuthentication(Port *port);


Re: krb_match_realm

From
Tom Lane
Date:
Magnus Hagander <magnus@hagander.net> writes:
> Attached patch implements krb_match_realm for krb5, gssapi and sspi per
> complaint from Henry. Comments welcome.

Minor gripe: "krb_match_realm" sounds like it should be a boolean:
do or don't check the realm.  Would just "krb_realm" be sensible?

Also the elog message texts need a bit of copy-editing.

            regards, tom lane

Re: krb_match_realm

From
Magnus Hagander
Date:
Tom Lane wrote:
> Magnus Hagander <magnus@hagander.net> writes:
>> Attached patch implements krb_match_realm for krb5, gssapi and sspi per
>> complaint from Henry. Comments welcome.
>
> Minor gripe: "krb_match_realm" sounds like it should be a boolean:
> do or don't check the realm.  Would just "krb_realm" be sensible?

Yeah, probably. I'll change it to that.


> Also the elog message texts need a bit of copy-editing.

Probably ;-) Got any specific hints, so I don't have to go through the
iteration twice?

//Magnus

Re: krb_match_realm

From
Tom Lane
Date:
Magnus Hagander <magnus@hagander.net> writes:
> Tom Lane wrote:
>> Also the elog message texts need a bit of copy-editing.

> Probably ;-) Got any specific hints, so I don't have to go through the
> iteration twice?

The one that caught my eye was

             "SSPI domain (%s) does and configured domain (%s) don't match",

            regards, tom lane

Re: krb_match_realm

From
Magnus Hagander
Date:
Tom Lane wrote:
> Magnus Hagander <magnus@hagander.net> writes:
>> Tom Lane wrote:
>>> Also the elog message texts need a bit of copy-editing.
>
>> Probably ;-) Got any specific hints, so I don't have to go through the
>> iteration twice?
>
> The one that caught my eye was
>
>              "SSPI domain (%s) does and configured domain (%s) don't match",

Wow, I need a break from the monitor, I think. I read over them all
again, and missed it again :-( Will fix.

//Magnus

Re: krb_match_realm

From
Magnus Hagander
Date:
Henry B. Hotz wrote:
> Thank you very much.  This helps, but I'm still evaluating how much.
>
> I *can* point at one problem though:  you do a strchr(gbuf.value, '@')
> and then error out if there isn't a Kerberos realm there.  In fact that
> is exactly the default username of at least one of the GSSAPI
> implementations I've tested if the realm is the same as the local
> default realm.

Eh, so how do we then determine the difference between local realm and
no realm given?


> I'm not entirely sure what the intended semantics of krb_match_realm
> are, but if you're trying to match the GSSAPI-authenticated name against
> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to construct
> that string, gss_import_name() it, and then gss_compare_name() the
> imported name with the authenticated name that GSSAPI already gave you.
> I know the API overhead of doing that is a PITA, but that's what's going
> to work.

Why?

(FWIW, it works perfectly fine in my test setups, so I'd really like to
know why this won't work)


> I also notice you have some code to do case insensitive name matching.
> I assume this is to take care of the fact that Microsoft Kerberos does
> case insensitive name matching (contrary to the standard and the other
> Kerberos implementations out there).  I suspect issues there, but it
> will be 3-6 months before I will have an environment where I can easily
> test this.  Most likely, the way to handle this is by figuring out what
> case Microsoft uses for each name inside the protocol and then
> pre-mapping to that case before feeding things to (non-Microsoft) GSSAPI.

Yes, it's for supporting Active Directory. It's there in the same way
it's there for krb5.


> I don't regard the case mapping issues as serious.  We may not have the
> intended level of Windows/Unix compatibility, but I don't expect other
> issues.  In other words I'm not even going to think about it until it's
> easy for me to investigate.

Note that it's turned *off* by default, so it shouldn't even affect you.


>> Attached patch implements krb_match_realm for krb5, gssapi and sspi per
>> complaint from Henry. Comments welcome.
>>
>> Working on documentation which will of course be ready when it's
>> committed :)
>>
>> Oh, and it changes the krb username handling to be the same as the
>> gssapi one. I've never heard of anybody actually using the other version
>> that it used to support, and the comment clearly states that it was
>> broken for the really complex scenarios anyway - something nobody has
>> complained about.
>
> Well, *I* complained about it.  ;-)

Um, not sure we're talking about the same thing. I know you complained
about the inability to match realm, but did you complain about the
inability to use things like full X.500 names as usernames?


//Magnus

Re: krb_match_realm

From
"Henry B. Hotz"
Date:
Thank you very much.  This helps, but I'm still evaluating how much.

I *can* point at one problem though:  you do a strchr(gbuf.value,
'@') and then error out if there isn't a Kerberos realm there.  In
fact that is exactly the default username of at least one of the
GSSAPI implementations I've tested if the realm is the same as the
local default realm.

I'm not entirely sure what the intended semantics of krb_match_realm
are, but if you're trying to match the GSSAPI-authenticated name
against "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
construct that string, gss_import_name() it, and then gss_compare_name
() the imported name with the authenticated name that GSSAPI already
gave you.  I know the API overhead of doing that is a PITA, but
that's what's going to work.

I also notice you have some code to do case insensitive name
matching.  I assume this is to take care of the fact that Microsoft
Kerberos does case insensitive name matching (contrary to the
standard and the other Kerberos implementations out there).  I
suspect issues there, but it will be 3-6 months before I will have an
environment where I can easily test this.  Most likely, the way to
handle this is by figuring out what case Microsoft uses for each name
inside the protocol and then pre-mapping to that case before feeding
things to (non-Microsoft) GSSAPI.

I don't regard the case mapping issues as serious.  We may not have
the intended level of Windows/Unix compatibility, but I don't expect
other issues.  In other words I'm not even going to think about it
until it's easy for me to investigate.

On Nov 1, 2007, at 5:27 AM, Magnus Hagander wrote:

> Attached patch implements krb_match_realm for krb5, gssapi and sspi
> per
> complaint from Henry. Comments welcome.
>
> Working on documentation which will of course be ready when it's
> committed :)
>
> Oh, and it changes the krb username handling to be the same as the
> gssapi one. I've never heard of anybody actually using the other
> version
> that it used to support, and the comment clearly states that it was
> broken for the really complex scenarios anyway - something nobody has
> complained about.

Well, *I* complained about it.  ;-)

Sorry if I'm the only one, but I can't change the deployment
environment I'm going to have to deal with.

> //Magnus
> Index: backend/libpq/auth.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/libpq/auth.c,v
> retrieving revision 1.156
> diff -c -r1.156 auth.c
> *** backend/libpq/auth.c    14 Sep 2007 15:58:02 -0000    1.156
> --- backend/libpq/auth.c    1 Nov 2007 11:28:54 -0000
> ***************
> *** 42,47 ****
> --- 42,48 ----
>   char       *pg_krb_srvnam;
>   bool        pg_krb_caseins_users;
>   char       *pg_krb_server_hostname = NULL;
> + char       *pg_krb_match_realm = NULL;
>
>   #ifdef USE_PAM
>   #ifdef HAVE_PAM_PAM_APPL_H
> ***************
> *** 103,132 ****
>   #endif
>
>   /*
> -  * pg_an_to_ln -- return the local name corresponding to an
> authentication
> -  *                  name
> -  *
> -  * XXX Assumes that the first aname component is the user name.
> This is NOT
> -  *       necessarily so, since an aname can actually be something
> out of your
> -  *       worst X.400 nightmare, like
> -  *          ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
> -  *       Note that the MIT an_to_ln code does the same thing if you
> don't
> -  *       provide an aname mapping database...it may be a better idea
> to use
> -  *       krb5_an_to_ln, except that it punts if multiple components
> are found,
> -  *       and we can't afford to punt.
> -  */
> - static char *
> - pg_an_to_ln(char *aname)
> - {
> -     char       *p;
> -
> -     if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
> -         *p = '\0';
> -     return aname;
> - }
> -
> -
> - /*
>    * Various krb5 state which is not connection specfic, and a flag to
>    * indicate whether we have initialised it yet.
>    */
> --- 104,109 ----
> ***************
> *** 216,221 ****
> --- 193,199 ----
>       krb5_auth_context auth_context = NULL;
>       krb5_ticket *ticket;
>       char       *kusername;
> +     char       *cp;
>
>       if (get_role_line(port->user_name) == NULL)
>           return STATUS_ERROR;
> ***************
> *** 240,247 ****
>        * The "client" structure comes out of the ticket and is therefore
>        * authenticated.  Use it to check the username obtained from the
>        * postmaster startup packet.
> -      *
> -      * I have no idea why this is considered necessary.
>        */
>   #if defined(HAVE_KRB5_TICKET_ENC_PART2)
>       retval = krb5_unparse_name(pg_krb5_context,
> --- 218,223 ----
> ***************
> *** 263,269 ****
>           return STATUS_ERROR;
>       }
>
> !     kusername = pg_an_to_ln(kusername);
>       if (pg_krb_caseins_users)
>           ret = pg_strncasecmp(port->user_name, kusername,
> SM_DATABASE_USER);
>       else
> --- 239,280 ----
>           return STATUS_ERROR;
>       }
>
> !     cp = strchr(kusername, '@');
> !     if (cp)
> !     {
> !         *cp = '\0';
> !         cp++;
> !
> !         if (pg_krb_match_realm != NULL && strlen(pg_krb_match_realm))
> !         {
> !             /* Match realm against configured */
> !             if (pg_krb_caseins_users)
> !                 ret = pg_strcasecmp(pg_krb_match_realm, cp);
> !             else
> !                 ret = strcmp(pg_krb_match_realm, cp);
> !
> !             if (ret)
> !             {
> !                 elog(DEBUG2,
> !                      "krb5 realm (%s) and configured realm (%s) don't match",
> !                      cp, pg_krb_match_realm);
> !
> !                 krb5_free_ticket(pg_krb5_context, ticket);
> !                 krb5_auth_con_free(pg_krb5_context, auth_context);
> !                 return STATUS_ERROR;
> !             }
> !         }
> !     }
> !     else if (pg_krb_match_realm && strlen(pg_krb_match_realm))

I assume this case is in the existing kerb5 method.

> !     {
> !         elog(DEBUG2,
> !              "krb5 did not return realm but realm matching was requested");
> !
> !         krb5_free_ticket(pg_krb5_context, ticket);
> !         krb5_auth_con_free(pg_krb5_context, auth_context);
> !         return STATUS_ERROR;
> !     }
> !
>       if (pg_krb_caseins_users)
>           ret = pg_strncasecmp(port->user_name, kusername,
> SM_DATABASE_USER);
>       else
> ***************
> *** 509,522 ****
>                        maj_stat, min_stat);
>
>       /*
> !      * Compare the part of the username that comes before the @
> !      * sign only (ignore realm). The GSSAPI libraries won't have
> !      * authenticated the user if he's from an invalid realm.
>        */
>       if (strchr(gbuf.value, '@'))
>       {
>           char *cp = strchr(gbuf.value, '@');
>           *cp = '\0';
>       }
>
>       if (pg_krb_caseins_users)
> --- 520,561 ----
>                        maj_stat, min_stat);
>
>       /*
> !      * Split the username at the realm separator
>        */
>       if (strchr(gbuf.value, '@'))
>       {
>           char *cp = strchr(gbuf.value, '@');
>           *cp = '\0';
> +         cp++;
> +
> +         if (pg_krb_match_realm != NULL && strlen(pg_krb_match_realm))
> +         {
> +             /*
> +              * Match the realm part of the name first
> +              */
> +             if (pg_krb_caseins_users)
> +                 ret = pg_strcasecmp(pg_krb_match_realm, cp);
> +             else
> +                 ret = strcmp(pg_krb_match_realm, cp);
> +
> +             if (ret)
> +             {
> +                 /* GSS realm does not match */
> +                 elog(DEBUG2,
> +                      "GSSAPI realm (%s) and configured realm (%s) don't match",
> +                      cp, pg_krb_match_realm);
> +                 gss_release_buffer(&lmin_s, &gbuf);
> +                 return STATUS_ERROR;
> +             }
> +         }
> +     }
> +     else if (pg_krb_match_realm && strlen(pg_krb_match_realm))
> +     {
> +         elog(DEBUG2,
> +              "GSSAPI did not return realm but realm matching was requested");
> +

Most likely reason is that the realm is the same as the local default
realm.  This shouldn't be an error.

> +         gss_release_buffer(&lmin_s, &gbuf);
> +         return STATUS_ERROR;
>       }
>
>       if (pg_krb_caseins_users)
> ***************
> *** 792,797 ****
> --- 831,851 ----
>
>       free(tokenuser);
>
> +     /*
> +      * Compare realm/domain if requested. In SSPI, always compare
> case insensitive.
> +      */
> +     if (pg_krb_match_realm && strlen(pg_krb_match_realm))
> +     {
> +         if (pg_strcasecmp(pg_krb_match_realm, domainname))
> +         {
> +             elog(DEBUG2,
> +                  "SSPI domain (%s) does and configured domain (%s) don't match",
> +                  domainname, pg_krb_match_realm);
> +
> +             return STATUS_ERROR;
> +         }
> +     }
> +
>       /*
>        * We have the username (without domain/realm) in accountname,
> compare
>        * to the supplied value. In SSPI, always compare case insensitive.
> Index: backend/utils/misc/guc.c
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v
> retrieving revision 1.423
> diff -c -r1.423 guc.c
> *** backend/utils/misc/guc.c    26 Sep 2007 22:36:30 -0000    1.423
> --- backend/utils/misc/guc.c    1 Nov 2007 11:28:54 -0000
> ***************
> *** 2044,2049 ****
> --- 2044,2059 ----
>       },
>
>       {
> +         {"krb_match_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY,
> +             gettext_noop("Sets realm to match Kerberos and GSSAPI users
> against."),
> +             NULL,
> +             GUC_SUPERUSER_ONLY
> +         },
> +         &pg_krb_match_realm,
> +         NULL, NULL, NULL
> +     },
> +
> +     {
>           {"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY,
>               gettext_noop("Sets the location of the Kerberos server key
> file."),
>               NULL,
> Index: backend/utils/misc/postgresql.conf.sample
> ===================================================================
> RCS file: /cvsroot/pgsql/src/backend/utils/misc/
> postgresql.conf.sample,v
> retrieving revision 1.231
> diff -c -r1.231 postgresql.conf.sample
> *** backend/utils/misc/postgresql.conf.sample    26 Sep 2007 22:36:30
> -0000    1.231
> --- backend/utils/misc/postgresql.conf.sample    1 Nov 2007 11:28:54
> -0000
> ***************
> *** 85,90 ****
> --- 85,91 ----
>   #krb_server_hostname = ''        # empty string matches any keytab entry
>                       # (change requires restart, kerberos only)
>   #krb_caseins_users = off        # (change requires restart)
> + #krb_match_realm = ''           # (change requires restart)
>
>   # - TCP Keepalives -
>   # see 'man 7 tcp' for details
> Index: include/libpq/auth.h
> ===================================================================
> RCS file: /cvsroot/pgsql/src/include/libpq/auth.h,v
> retrieving revision 1.33
> diff -c -r1.33 auth.h
> *** include/libpq/auth.h    5 Jan 2007 22:19:55 -0000    1.33
> --- include/libpq/auth.h    1 Nov 2007 11:28:54 -0000
> ***************
> *** 20,25 ****
> --- 20,26 ----
>   extern char *pg_krb_srvnam;
>   extern bool pg_krb_caseins_users;
>   extern char *pg_krb_server_hostname;
> + extern char *pg_krb_match_realm;
>
>   extern void ClientAuthentication(Port *port);
>


Re: krb_match_realm

From
"Henry B. Hotz"
Date:
On Nov 1, 2007, at 1:40 PM, Magnus Hagander wrote:

> Henry B. Hotz wrote:
>> Thank you very much.  This helps, but I'm still evaluating how much.
>>
>> I *can* point at one problem though:  you do a strchr(gbuf.value,
>> '@')
>> and then error out if there isn't a Kerberos realm there.  In fact
>> that
>> is exactly the default username of at least one of the GSSAPI
>> implementations I've tested if the realm is the same as the local
>> default realm.
>
> Eh, so how do we then determine the difference between local realm and
> no realm given?

Well, what I've seen is:  no realm given if and only if the default
local realm matches the realm for the GSSAPI username.  I don't think
that's guaranteed.

>> I'm not entirely sure what the intended semantics of krb_match_realm
>> are, but if you're trying to match the GSSAPI-authenticated name
>> against
>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
>> construct
>> that string, gss_import_name() it, and then gss_compare_name() the
>> imported name with the authenticated name that GSSAPI already gave
>> you.
>> I know the API overhead of doing that is a PITA, but that's what's
>> going
>> to work.
>
> Why?

Because if we're using the GSSAPI then we need to use the properties
defined by the GSSAPI, and not depend on observed behavior of
specific implementations of specific mechanisms.  Otherwise things
will be non-portable or unreliable in ways that may be non-obvious.

In particular gss_display_name() produces a character string intended
for display to a human being.  It is *NOT* intended for access
control.  As another example, Heimdal gss_display_name() puts '\'
escapes in front of special characters in the username.  I don't
think it's worth writing special case code for that either.

The standard defines two ways to do comparisons for access control.
We should use one of them.  Anything else is going to be more work
and less reliable.

> (FWIW, it works perfectly fine in my test setups, so I'd really
> like to
> know why this won't work)

I may be able to flag specific failures, as I've done here, but I'm
sure I can't imagine all the ways that evading the standard might
cause problems.  Perhaps that's not helpful or not what you want, but
it's all I can do.

>> I also notice you have some code to do case insensitive name
>> matching.
>> I assume this is to take care of the fact that Microsoft Kerberos
>> does
>> case insensitive name matching (contrary to the standard and the
>> other
>> Kerberos implementations out there).  I suspect issues there, but it
>> will be 3-6 months before I will have an environment where I can
>> easily
>> test this.  Most likely, the way to handle this is by figuring out
>> what
>> case Microsoft uses for each name inside the protocol and then
>> pre-mapping to that case before feeding things to (non-Microsoft)
>> GSSAPI.
>
> Yes, it's for supporting Active Directory. It's there in the same way
> it's there for krb5.
>
>
>> I don't regard the case mapping issues as serious.  We may not
>> have the
>> intended level of Windows/Unix compatibility, but I don't expect
>> other
>> issues.  In other words I'm not even going to think about it until
>> it's
>> easy for me to investigate.
>
> Note that it's turned *off* by default, so it shouldn't even affect
> you.

Not my current concern.  If I find a problem, then I'll be in a
position to define how it ought to be done (*IF* it needs to be done
differently).

>>> Attached patch implements krb_match_realm for krb5, gssapi and
>>> sspi per
>>> complaint from Henry. Comments welcome.
>>>
>>> Working on documentation which will of course be ready when it's
>>> committed :)
>>>
>>> Oh, and it changes the krb username handling to be the same as the
>>> gssapi one. I've never heard of anybody actually using the other
>>> version
>>> that it used to support, and the comment clearly states that it was
>>> broken for the really complex scenarios anyway - something nobody
>>> has
>>> complained about.
>>
>> Well, *I* complained about it.  ;-)
>
> Um, not sure we're talking about the same thing. I know you complained
> about the inability to match realm, but did you complain about the
> inability to use things like full X.500 names as usernames?
>
>
> //Magnus

Well, it's not a high priority for me, but there is a GSSAPI
mechanism called SPKM which uses X500-syle names (X509 certificate
subject names to be precise).  If we use gss_name_compare() properly
then it should "just work".

Re: krb_match_realm

From
Magnus Hagander
Date:
Henry B. Hotz wrote:
>
> On Nov 1, 2007, at 1:40 PM, Magnus Hagander wrote:
>
>> Henry B. Hotz wrote:
>>> Thank you very much.  This helps, but I'm still evaluating how much.
>>>
>>> I *can* point at one problem though:  you do a strchr(gbuf.value, '@')
>>> and then error out if there isn't a Kerberos realm there.  In fact that
>>> is exactly the default username of at least one of the GSSAPI
>>> implementations I've tested if the realm is the same as the local
>>> default realm.
>>
>> Eh, so how do we then determine the difference between local realm and
>> no realm given?
>
> Well, what I've seen is:  no realm given if and only if the default
> local realm matches the realm for the GSSAPI username.  I don't think
> that's guaranteed.

Irrk. Very much irrk.


>>> I'm not entirely sure what the intended semantics of krb_match_realm
>>> are, but if you're trying to match the GSSAPI-authenticated name against
>>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to construct
>>> that string, gss_import_name() it, and then gss_compare_name() the
>>> imported name with the authenticated name that GSSAPI already gave you.
>>> I know the API overhead of doing that is a PITA, but that's what's going
>>> to work.
>>
>> Why?
>
> Because if we're using the GSSAPI then we need to use the properties
> defined by the GSSAPI, and not depend on observed behavior of specific
> implementations of specific mechanisms.  Otherwise things will be
> non-portable or unreliable in ways that may be non-obvious.
>
> In particular gss_display_name() produces a character string intended
> for display to a human being.  It is *NOT* intended for access control.
> As another example, Heimdal gss_display_name() puts '\' escapes in front
> of special characters in the username.  I don't think it's worth writing
> special case code for that either.

Ok. I can see that point. However, if you have those characters in your
username, you may have other problems as well :-)

Is there some other way to actually get the username from gss? I mean,
if we *didn't* get it from the startup packet, how would we ever be able
to determine what user logged in?


> The standard defines two ways to do comparisons for access control.  We
> should use one of them.  Anything else is going to be more work and less
> reliable.

What's the other way then?

Last I checked there was no way to do case insensitive matching on
gss_compare_name() but I could be on the wrong docs? Finding any kind of
consistent docs for this stuff isn't exactly easy.
Because we *must* have the ability to do case insensitive matching, or
it *will* break on Windows.


> Well, it's not a high priority for me, but there is a GSSAPI mechanism
> called SPKM which uses X500-syle names (X509 certificate subject names
> to be precise).  If we use gss_name_compare() properly then it should
> "just work".

I'm unsure if PostgreSQL in general is prepared to deal with such
usernames. You'd certainly have to verify that stuff before anything
would "just work".

//Magnus

Re: krb_match_realm

From
"Henry B. Hotz"
Date:
On Nov 2, 2007, at 8:38 AM, Magnus Hagander wrote:

> Henry B. Hotz wrote:
>>
>> On Nov 1, 2007, at 1:40 PM, Magnus Hagander wrote:
>>
>>> Henry B. Hotz wrote:
>>>> Thank you very much.  This helps, but I'm still evaluating how
>>>> much.
>>>>
>>>> I *can* point at one problem though:  you do a strchr
>>>> (gbuf.value, '@')
>>>> and then error out if there isn't a Kerberos realm there.  In
>>>> fact that
>>>> is exactly the default username of at least one of the GSSAPI
>>>> implementations I've tested if the realm is the same as the local
>>>> default realm.
>>>
>>> Eh, so how do we then determine the difference between local
>>> realm and
>>> no realm given?
>>
>> Well, what I've seen is:  no realm given if and only if the default
>> local realm matches the realm for the GSSAPI username.  I don't think
>> that's guaranteed.
>
> Irrk. Very much irrk.
>
>
>>>> I'm not entirely sure what the intended semantics of
>>>> krb_match_realm
>>>> are, but if you're trying to match the GSSAPI-authenticated name
>>>> against
>>>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
>>>> construct
>>>> that string, gss_import_name() it, and then gss_compare_name() the
>>>> imported name with the authenticated name that GSSAPI already
>>>> gave you.
>>>> I know the API overhead of doing that is a PITA, but that's
>>>> what's going
>>>> to work.
>>>
>>> Why?
>>
>> Because if we're using the GSSAPI then we need to use the properties
>> defined by the GSSAPI, and not depend on observed behavior of
>> specific
>> implementations of specific mechanisms.  Otherwise things will be
>> non-portable or unreliable in ways that may be non-obvious.
>>
>> In particular gss_display_name() produces a character string intended
>> for display to a human being.  It is *NOT* intended for access
>> control.
>> As another example, Heimdal gss_display_name() puts '\' escapes in
>> front
>> of special characters in the username.  I don't think it's worth
>> writing
>> special case code for that either.
>
> Ok. I can see that point. However, if you have those characters in
> your
> username, you may have other problems as well :-)

Yeah.  Not many people put spaces inside usernames.

> Is there some other way to actually get the username from gss? I mean,
> if we *didn't* get it from the startup packet, how would we ever be
> able
> to determine what user logged in?

gss_export_name(), but what it returns is supposed to be an opaque
binary blob.

It's guaranteed to produce a unique, canonicalized name based on the
specific mechanism in use.  It is suitable for memcmp().  The
exported name will re-import.  Section 3.10 of rfc 2744 describes all
this, and appears to be clearer than the Sun document I pointed you
at.  Certainly it's more concise.  YMMV.

memcmp() on exported names will only be true if everyone uses the
same gss mechanism.  (OK, the only one we care about is kerberos.)
In contrast it's possible that gss_compare_name() would say that
"uid=smith,ou=People,dc=example,dc=com" is the same as
smith@EXAMPLE.COM.

>> The standard defines two ways to do comparisons for access
>> control.  We
>> should use one of them.  Anything else is going to be more work
>> and less
>> reliable.
>
> What's the other way then?
>
> Last I checked there was no way to do case insensitive matching on
> gss_compare_name() but I could be on the wrong docs? Finding any
> kind of
> consistent docs for this stuff isn't exactly easy.
> Because we *must* have the ability to do case insensitive matching, or
> it *will* break on Windows.

No gss_compare_name() is case sensitive.  I think the way to do it is
to know what case Microsoft is going to use and pre-map everything to
that case (before you do a gss_import_name()).  I *think* Microsoft
will use upper case for the service name so we will need to change
from "postgres" to "POSTGRES" as the default name in service
principals.  I've seen places where they may be using lower case
realm names (which makes *NO* sense to me).

Absent an environment where I can actually look at all these things,
my only point of reference is mod_auth_kerb, and the issues reported
with it.  I know an upper case "HTTP" is needed to interoperate with
windows clients.  An upper case realm name seems to be OK, as is a
lower case server name in the second component.  The actual usernames
seem to be lower case, but that's not the concern of the
mod_auth_kerb developers since the deployer just needs to put in
whatever matches.

I assume in AD you can't create both "smith" and "Smith", but can you
create the latter at all?  If you do, does AD remember the case for
display purposes?  Here at JPL usernames are lower case, and I don't
think we allow anything special but hyphens in them, so I'm not
likely to see a lot of the possible corner cases.

I think you can upper case the service name, lower case the server
name, upper case the realm name, and lower case the user name.  If
you can create "Smith" in AD and the user gets authenticated as
"Smith@EXAMPLE.COM" at the protocol level then that won't work though.

I'm actually trying to write some Kerberos principal to X500 name
translation code for mod_auth_kerb right now, and I'm not happy with
my options.  I'm probably going to wind up doing something that I
would tell you is improper, not general purpose, fragile, and
shouldn't be done.  |-P  At least it's completely legitimate for me
to assume that the only mechanism in use is the Kerberos one (since
SPNEGO will have negotiated to kerberos before I need to worry about
it).  This is part of building up an example SAPP server (Solaris
Apache Postgres Perl, you've heard of LAMP servers I assume).

>> Well, it's not a high priority for me, but there is a GSSAPI
>> mechanism
>> called SPKM which uses X500-syle names (X509 certificate subject
>> names
>> to be precise).  If we use gss_name_compare() properly then it should
>> "just work".
>
> I'm unsure if PostgreSQL in general is prepared to deal with such
> usernames. You'd certainly have to verify that stuff before anything
> would "just work".
>
> //Magnus

Well it might be nice if we could use the same logic and conventions
to support TLS client identities as SPKM ones.  Shouldn't be hard.
(Famous last words.)

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: krb_match_realm

From
"Henry B. Hotz"
Date:
On Nov 1, 2007, at 6:33 AM, Tom Lane wrote:

> Magnus Hagander <magnus@hagander.net> writes:
>> Tom Lane wrote:
>>> Also the elog message texts need a bit of copy-editing.
>
>> Probably ;-) Got any specific hints, so I don't have to go through
>> the
>> iteration twice?
>
> The one that caught my eye was
>
>              "SSPI domain (%s) does and configured domain (%s)
> don't match",
>
>             regards, tom lane

s/does //

I assume that's your point?

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: krb_match_realm

From
Magnus Hagander
Date:
Henry B. Hotz wrote:
>
> On Nov 1, 2007, at 6:33 AM, Tom Lane wrote:
>
>> Magnus Hagander <magnus@hagander.net> writes:
>>> Tom Lane wrote:
>>>> Also the elog message texts need a bit of copy-editing.
>>
>>> Probably ;-) Got any specific hints, so I don't have to go through the
>>> iteration twice?
>>
>> The one that caught my eye was
>>
>>              "SSPI domain (%s) does and configured domain (%s) don't
>> match",
>>
>>             regards, tom lane
>
> s/does //
>
> I assume that's your point?

Yup, thanks. I fixed that per Toms earlier comment.

I'll get back to you on the other email tomorrow, it's becoming late
friday evening here now :-)

//Magnus

Re: krb_match_realm

From
Magnus Hagander
Date:
On Fri, Nov 02, 2007 at 11:23:30AM -0700, Henry B. Hotz wrote:
> >>>>I'm not entirely sure what the intended semantics of
> >>>>krb_match_realm
> >>>>are, but if you're trying to match the GSSAPI-authenticated name
> >>>>against
> >>>>"value_of(PGUSER)@value_of(krb_match_realm)" then you need to
> >>>>construct
> >>>>that string, gss_import_name() it, and then gss_compare_name() the
> >>>>imported name with the authenticated name that GSSAPI already
> >>>>gave you.
> >>>>I know the API overhead of doing that is a PITA, but that's
> >>>>what's going
> >>>>to work.
> >>>
> >>>Why?
> >>
> >>Because if we're using the GSSAPI then we need to use the properties
> >>defined by the GSSAPI, and not depend on observed behavior of
> >>specific
> >>implementations of specific mechanisms.  Otherwise things will be
> >>non-portable or unreliable in ways that may be non-obvious.
> >>
> >>In particular gss_display_name() produces a character string intended
> >>for display to a human being.  It is *NOT* intended for access
> >>control.
> >>As another example, Heimdal gss_display_name() puts '\' escapes in
> >>front
> >>of special characters in the username.  I don't think it's worth
> >>writing
> >>special case code for that either.
> >
> >Ok. I can see that point. However, if you have those characters in
> >your
> >username, you may have other problems as well :-)
>
> Yeah.  Not many people put spaces inside usernames.

I think we can easily get away with not covering that case.


> >Is there some other way to actually get the username from gss? I mean,
> >if we *didn't* get it from the startup packet, how would we ever be
> >able
> >to determine what user logged in?
>
> gss_export_name(), but what it returns is supposed to be an opaque
> binary blob.
>
> It's guaranteed to produce a unique, canonicalized name based on the
> specific mechanism in use.  It is suitable for memcmp().  The
> exported name will re-import.  Section 3.10 of rfc 2744 describes all
> this, and appears to be clearer than the Sun document I pointed you
> at.  Certainly it's more concise.  YMMV.

Hmm. But it doesn't serve our purpose.


> memcmp() on exported names will only be true if everyone uses the
> same gss mechanism.  (OK, the only one we care about is kerberos.)
> In contrast it's possible that gss_compare_name() would say that
> "uid=smith,ou=People,dc=example,dc=com" is the same as
> smith@EXAMPLE.COM.

No, memcmp()ing two separate strings (userid + match realm) with an opaque
binary blob certainly won't help us at all.


> >>The standard defines two ways to do comparisons for access
> >>control.  We
> >>should use one of them.  Anything else is going to be more work
> >>and less
> >>reliable.
> >
> >What's the other way then?
> >
> >Last I checked there was no way to do case insensitive matching on
> >gss_compare_name() but I could be on the wrong docs? Finding any
> >kind of
> >consistent docs for this stuff isn't exactly easy.
> >Because we *must* have the ability to do case insensitive matching, or
> >it *will* break on Windows.
>
> No gss_compare_name() is case sensitive.  I think the way to do it is
> to know what case Microsoft is going to use and pre-map everything to
> that case (before you do a gss_import_name()).  I *think* Microsoft
> will use upper case for the service name so we will need to change
> from "postgres" to "POSTGRES" as the default name in service
> principals.  I've seen places where they may be using lower case
> realm names (which makes *NO* sense to me).

No. Microsoft will send you whatever case the user put into the login box
when he logged into windows. It's case-*preserving*, but case-insensitive.

However, AD itself requires uppercase service name, but that's a different
thing.

> Absent an environment where I can actually look at all these things,
> my only point of reference is mod_auth_kerb, and the issues reported
> with it.  I know an upper case "HTTP" is needed to interoperate with
> windows clients.  An upper case realm name seems to be OK, as is a
> lower case server name in the second component.  The actual usernames
> seem to be lower case, but that's not the concern of the
> mod_auth_kerb developers since the deployer just needs to put in
> whatever matches.

The usernames depend on what the client puts in. It's generally a big
problem because a lot of krb-aware applications can't deal with it.

> I assume in AD you can't create both "smith" and "Smith", but can you
> create the latter at all?  If you do, does AD remember the case for
> display purposes?  Here at JPL usernames are lower case, and I don't
> think we allow anything special but hyphens in them, so I'm not
> likely to see a lot of the possible corner cases.

You can and it remembers. But it has no effect on what is sent ni the
kerberos packets - what's sent there is what the user typed in. Yes, that
sucks bad, but that's how it is.

> I think you can upper case the service name, lower case the server
> name, upper case the realm name, and lower case the user name.  If
> you can create "Smith" in AD and the user gets authenticated as
> "Smith@EXAMPLE.COM" at the protocol level then that won't work though.

Which is how it is :-(



From what I can tell, the least bad way to do it is still the patch that
sits in the queue now (pending changes based on Stephens comments, but
those are a differnt thing)

//Magnus

Re: krb_match_realm

From
"Henry B. Hotz"
Date:
On Nov 6, 2007, at 6:27 AM, Magnus Hagander wrote:

> On Fri, Nov 02, 2007 at 11:23:30AM -0700, Henry B. Hotz wrote:
>>>>>> I'm not entirely sure what the intended semantics of
>>>>>> krb_match_realm
>>>>>> are, but if you're trying to match the GSSAPI-authenticated name
>>>>>> against
>>>>>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
>>>>>> construct
>>>>>> that string, gss_import_name() it, and then gss_compare_name()
>>>>>> the
>>>>>> imported name with the authenticated name that GSSAPI already
>>>>>> gave you.
>>>>>> I know the API overhead of doing that is a PITA, but that's
>>>>>> what's going
>>>>>> to work.
>>>>>
>>>>> Why?
>>>>
>>>> Because if we're using the GSSAPI then we need to use the
>>>> properties
>>>> defined by the GSSAPI, and not depend on observed behavior of
>>>> specific
>>>> implementations of specific mechanisms.  Otherwise things will be
>>>> non-portable or unreliable in ways that may be non-obvious.
>>>>
>>>> In particular gss_display_name() produces a character string
>>>> intended
>>>> for display to a human being.  It is *NOT* intended for access
>>>> control.
>>>> As another example, Heimdal gss_display_name() puts '\' escapes in
>>>> front
>>>> of special characters in the username.  I don't think it's worth
>>>> writing
>>>> special case code for that either.
>>>
>>> Ok. I can see that point. However, if you have those characters in
>>> your
>>> username, you may have other problems as well :-)
>>
>> Yeah.  Not many people put spaces inside usernames.
>
> I think we can easily get away with not covering that case.

*sigh*  Yeah, maybe we have to live with it.

>>> Is there some other way to actually get the username from gss? I
>>> mean,
>>> if we *didn't* get it from the startup packet, how would we ever be
>>> able
>>> to determine what user logged in?
>>
>> gss_export_name(), but what it returns is supposed to be an opaque
>> binary blob.
>>
>> It's guaranteed to produce a unique, canonicalized name based on the
>> specific mechanism in use.  It is suitable for memcmp().  The
>> exported name will re-import.  Section 3.10 of rfc 2744 describes all
>> this, and appears to be clearer than the Sun document I pointed you
>> at.  Certainly it's more concise.  YMMV.
>
> Hmm. But it doesn't serve our purpose.

Well, it *might* work to do a memcmp() of tolower() of the blobs.

>> memcmp() on exported names will only be true if everyone uses the
>> same gss mechanism.  (OK, the only one we care about is kerberos.)
>> In contrast it's possible that gss_compare_name() would say that
>> "uid=smith,ou=People,dc=example,dc=com" is the same as
>> smith@EXAMPLE.COM.
>
> No, memcmp()ing two separate strings (userid + match realm) with an
> opaque
> binary blob certainly won't help us at all.
>
>
>>>> The standard defines two ways to do comparisons for access
>>>> control.  We
>>>> should use one of them.  Anything else is going to be more work
>>>> and less
>>>> reliable.
>>>
>>> What's the other way then?
>>>
>>> Last I checked there was no way to do case insensitive matching on
>>> gss_compare_name() but I could be on the wrong docs? Finding any
>>> kind of
>>> consistent docs for this stuff isn't exactly easy.
>>> Because we *must* have the ability to do case insensitive
>>> matching, or
>>> it *will* break on Windows.
>>
>> No gss_compare_name() is case sensitive.  I think the way to do it is
>> to know what case Microsoft is going to use and pre-map everything to
>> that case (before you do a gss_import_name()).  I *think* Microsoft
>> will use upper case for the service name so we will need to change
>> from "postgres" to "POSTGRES" as the default name in service
>> principals.  I've seen places where they may be using lower case
>> realm names (which makes *NO* sense to me).
>
> No. Microsoft will send you whatever case the user put into the
> login box
> when he logged into windows. It's case-*preserving*, but case-
> insensitive.

That can't be entirely true.  Maybe it's true for Microsoft's RC4
enctype, but it can't be true for the des-cbc-md5 enctype.  The
protocol runs with some case, and the question is what case it uses
when it matters.

> However, AD itself requires uppercase service name, but that's a
> different
> thing.

OK.

>> Absent an environment where I can actually look at all these things,
>> my only point of reference is mod_auth_kerb, and the issues reported
>> with it.  I know an upper case "HTTP" is needed to interoperate with
>> windows clients.  An upper case realm name seems to be OK, as is a
>> lower case server name in the second component.  The actual usernames
>> seem to be lower case, but that's not the concern of the
>> mod_auth_kerb developers since the deployer just needs to put in
>> whatever matches.
>
> The usernames depend on what the client puts in. It's generally a big
> problem because a lot of krb-aware applications can't deal with it.

I'd bet that the authenticated username in the service ticket is
always a specific case.  I'd also bet that it's whatever case the
username was created with, e.g. "Smith".  Without being able to
inspect packets myself I'm only guessing though.

>> I assume in AD you can't create both "smith" and "Smith", but can you
>> create the latter at all?  If you do, does AD remember the case for
>> display purposes?  Here at JPL usernames are lower case, and I don't
>> think we allow anything special but hyphens in them, so I'm not
>> likely to see a lot of the possible corner cases.
>
> You can and it remembers. But it has no effect on what is sent ni the
> kerberos packets - what's sent there is what the user typed in.
> Yes, that
> sucks bad, but that's how it is.

Hmmm.  See above.  It isn't possible to make it irrelevant
everywhere, unless you only use RC4.  Vista uses AES so it looses
that loophole though.

Expanding a bit:  I can believe that the client uses the entered case
in the AS_REQ.  It's even possible that the DC follows suit for parts
of the AS_REP.  However I doubt that a non-Windows Kerberos client
can do even that initial exchange unless the case of the username
matches properly because the username is part of the salt used to
convert passwords to keys.

For a non-windows client I think you can assume the case will always
match.

The question is what does the DC put in the service ticket as the
authenticated username.  (A secondary question is if it's different
if the original authentication was to a non-Windows Kerberos
server.)  I would bet that it puts the originally defined case in the
service ticket (at least in the secondary case).

I wonder if case conversion is the right way to create compatibility
with GSSAPI though.  If the user always comes in with a specific case
then shouldn't we instead find a way to make sure the PG user is
created with the corresponding case?  There are several ways you can
test for the existence of a user in a Kerberos service, for example
kinit with a garbage password will give different errors.

If you think a hook in the add user code is in scope I can
investigate if there is a way to verify the existence of a username
with the GSSAPI instead of the Kerberos API.

It would be really easy to inspect what happens with tcpdump/snoop
and Ethereal, but that's the only way I can see to really answer
these questions.

>> I think you can upper case the service name, lower case the server
>> name, upper case the realm name, and lower case the user name.  If
>> you can create "Smith" in AD and the user gets authenticated as
>> "Smith@EXAMPLE.COM" at the protocol level then that won't work
>> though.
>
> Which is how it is :-(

I can only

> From what I can tell, the least bad way to do it is still the patch
> that
> sits in the queue now (pending changes based on Stephens comments, but
> those are a differnt thing)
>
> //Magnus

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu



Re: krb_match_realm

From
Magnus Hagander
Date:
On Tue, 2007-11-06 at 18:10 -0800, Henry B. Hotz wrote:
> On Nov 6, 2007, at 6:27 AM, Magnus Hagander wrote:
> > On Fri, Nov 02, 2007 at 11:23:30AM -0700, Henry B. Hotz wrote:
> >>>>>> I'm not entirely sure what the intended semantics of
> >>>>>> krb_match_realm
> >>>>>> are, but if you're trying to match the GSSAPI-authenticated name
> >>>>>> against
> >>>>>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
> >>>>>> construct
> >>>>>> that string, gss_import_name() it, and then gss_compare_name()
> >>>>>> the
> >>>>>> imported name with the authenticated name that GSSAPI already
> >>>>>> gave you.
> >>>>>> I know the API overhead of doing that is a PITA, but that's
> >>>>>> what's going
> >>>>>> to work.
> >>>>>
> >>>>> Why?
> >>>>
> >>>> Because if we're using the GSSAPI then we need to use the
> >>>> properties
> >>>> defined by the GSSAPI, and not depend on observed behavior of
> >>>> specific
> >>>> implementations of specific mechanisms.  Otherwise things will be
> >>>> non-portable or unreliable in ways that may be non-obvious.
> >>>>
> >>>> In particular gss_display_name() produces a character string
> >>>> intended
> >>>> for display to a human being.  It is *NOT* intended for access
> >>>> control.
> >>>> As another example, Heimdal gss_display_name() puts '\' escapes in
> >>>> front
> >>>> of special characters in the username.  I don't think it's worth
> >>>> writing
> >>>> special case code for that either.
> >>>
> >>> Ok. I can see that point. However, if you have those characters in
> >>> your
> >>> username, you may have other problems as well :-)
> >>
> >> Yeah.  Not many people put spaces inside usernames.
> >
> > I think we can easily get away with not covering that case.
>
> *sigh*  Yeah, maybe we have to live with it.

Yeah, that's kind of where I'm going - it's certainly not perfect, but
it's the least bad one.

Given this, I'll go ahead with the current version of the patch, and we
can consider making better changes at a later time assuming we figure
them out. It's still a lot better than what's in there now, and it
*will* cover the vast majority of cases.

I'll address Stephens comments separately.


> >> It's guaranteed to produce a unique, canonicalized name based on the
> >> specific mechanism in use.  It is suitable for memcmp().  The
> >> exported name will re-import.  Section 3.10 of rfc 2744 describes all
> >> this, and appears to be clearer than the Sun document I pointed you
> >> at.  Certainly it's more concise.  YMMV.
> >
> > Hmm. But it doesn't serve our purpose.
>
> Well, it *might* work to do a memcmp() of tolower() of the blobs.

Right, but if they're defined as blobs, that's even less safe than we
did before.


> >> No gss_compare_name() is case sensitive.  I think the way to do it is
> >> to know what case Microsoft is going to use and pre-map everything to
> >> that case (before you do a gss_import_name()).  I *think* Microsoft
> >> will use upper case for the service name so we will need to change
> >> from "postgres" to "POSTGRES" as the default name in service
> >> principals.  I've seen places where they may be using lower case
> >> realm names (which makes *NO* sense to me).
> >
> > No. Microsoft will send you whatever case the user put into the
> > login box
> > when he logged into windows. It's case-*preserving*, but case-
> > insensitive.
>
> That can't be entirely true.  Maybe it's true for Microsoft's RC4
> enctype, but it can't be true for the des-cbc-md5 enctype.  The
> protocol runs with some case, and the question is what case it uses
> when it matters.

Haven't protocol-traced it, but our systems use des-cbc-md5. It's the
only one I managed to get working with pg server running on Linux and
clients running on Windows. And in this case, the case insensitive/case
preserving behavior is a fact.



> >> I assume in AD you can't create both "smith" and "Smith", but can
> you
> >> create the latter at all?  If you do, does AD remember the case for
> >> display purposes?  Here at JPL usernames are lower case, and I don't
> >> think we allow anything special but hyphens in them, so I'm not
> >> likely to see a lot of the possible corner cases.
> >
> > You can and it remembers. But it has no effect on what is sent ni the
> > kerberos packets - what's sent there is what the user typed in.
> > Yes, that
> > sucks bad, but that's how it is.
>
> Hmmm.  See above.  It isn't possible to make it irrelevant
> everywhere, unless you only use RC4.  Vista uses AES so it looses
> that loophole though.
>
Yeah, I haven't tried Vista at all. I've only done 2000, XP and 2003.


> I wonder if case conversion is the right way to create compatibility
> with GSSAPI though.  If the user always comes in with a specific case
> then shouldn't we instead find a way to make sure the PG user is
> created with the corresponding case?  There are several ways you can
> test for the existence of a user in a Kerberos service, for example
> kinit with a garbage password will give different errors.

That's why we have krb5_caseninsens_users - to let the user choose it.


//Magnus


Re: krb_match_realm

From
"Henry B. Hotz"
Date:
On Nov 9, 2007, at 5:24 AM, Magnus Hagander wrote:

>
> On Tue, 2007-11-06 at 18:10 -0800, Henry B. Hotz wrote:
>> On Nov 6, 2007, at 6:27 AM, Magnus Hagander wrote:
>>> On Fri, Nov 02, 2007 at 11:23:30AM -0700, Henry B. Hotz wrote:
>>>>>>>> I'm not entirely sure what the intended semantics of
>>>>>>>> krb_match_realm
>>>>>>>> are, but if you're trying to match the GSSAPI-authenticated
>>>>>>>> name
>>>>>>>> against
>>>>>>>> "value_of(PGUSER)@value_of(krb_match_realm)" then you need to
>>>>>>>> construct
>>>>>>>> that string, gss_import_name() it, and then gss_compare_name()
>>>>>>>> the
>>>>>>>> imported name with the authenticated name that GSSAPI already
>>>>>>>> gave you.
>>>>>>>> I know the API overhead of doing that is a PITA, but that's
>>>>>>>> what's going
>>>>>>>> to work.
>>>>>>>
>>>>>>> Why?
>>>>>>
>>>>>> Because if we're using the GSSAPI then we need to use the
>>>>>> properties
>>>>>> defined by the GSSAPI, and not depend on observed behavior of
>>>>>> specific
>>>>>> implementations of specific mechanisms.  Otherwise things will be
>>>>>> non-portable or unreliable in ways that may be non-obvious.
>>>>>>
>>>>>> In particular gss_display_name() produces a character string
>>>>>> intended
>>>>>> for display to a human being.  It is *NOT* intended for access
>>>>>> control.
>>>>>> As another example, Heimdal gss_display_name() puts '\'
>>>>>> escapes in
>>>>>> front
>>>>>> of special characters in the username.  I don't think it's worth
>>>>>> writing
>>>>>> special case code for that either.
>>>>>
>>>>> Ok. I can see that point. However, if you have those characters in
>>>>> your
>>>>> username, you may have other problems as well :-)
>>>>
>>>> Yeah.  Not many people put spaces inside usernames.
>>>
>>> I think we can easily get away with not covering that case.
>>
>> *sigh*  Yeah, maybe we have to live with it.
>
> Yeah, that's kind of where I'm going - it's certainly not perfect, but
> it's the least bad one.
>
> Given this, I'll go ahead with the current version of the patch,
> and we
> can consider making better changes at a later time assuming we figure
> them out. It's still a lot better than what's in there now, and it
> *will* cover the vast majority of cases.
>
> I'll address Stephens comments separately.
>
>
>>>> It's guaranteed to produce a unique, canonicalized name based on
>>>> the
>>>> specific mechanism in use.  It is suitable for memcmp().  The
>>>> exported name will re-import.  Section 3.10 of rfc 2744
>>>> describes all
>>>> this, and appears to be clearer than the Sun document I pointed you
>>>> at.  Certainly it's more concise.  YMMV.
>>>
>>> Hmm. But it doesn't serve our purpose.
>>
>> Well, it *might* work to do a memcmp() of tolower() of the blobs.
>
> Right, but if they're defined as blobs, that's even less safe than we
> did before.
>
>
>>>> No gss_compare_name() is case sensitive.  I think the way to do
>>>> it is
>>>> to know what case Microsoft is going to use and pre-map
>>>> everything to
>>>> that case (before you do a gss_import_name()).  I *think* Microsoft
>>>> will use upper case for the service name so we will need to change
>>>> from "postgres" to "POSTGRES" as the default name in service
>>>> principals.  I've seen places where they may be using lower case
>>>> realm names (which makes *NO* sense to me).
>>>
>>> No. Microsoft will send you whatever case the user put into the
>>> login box
>>> when he logged into windows. It's case-*preserving*, but case-
>>> insensitive.
>>
>> That can't be entirely true.  Maybe it's true for Microsoft's RC4
>> enctype, but it can't be true for the des-cbc-md5 enctype.  The
>> protocol runs with some case, and the question is what case it uses
>> when it matters.
>
> Haven't protocol-traced it, but our systems use des-cbc-md5. It's the
> only one I managed to get working with pg server running on Linux and
> clients running on Windows. And in this case, the case insensitive/
> case
> preserving behavior is a fact.

I was thinking that the case of the principal affects the protocol,
but actually I guess that's only true for the string-to-key
function.  That would affect a user's ability to get the initial tgt,
but not how service tickets are handled.  Given case preservation,
that would guarantee the correct case for a non-Windows client (for
both Windows and Unix servers), but nowhere else.

Kind of blows the SSPI/GSSAPI compatibility that's supposed to
exist.  I think I may start a discussion of this on an IETF list in
case people want to update the GSSAPI standard.

I suppose you win.  Just do a gss_display_name() and do a case
insensitive compare.  I can't do any independent verification.
*bleah*  Can you leave the compare inside gssapi if it's not  case
insensitive though?

>>>> I assume in AD you can't create both "smith" and "Smith", but can
>> you
>>>> create the latter at all?  If you do, does AD remember the case for
>>>> display purposes?  Here at JPL usernames are lower case, and I
>>>> don't
>>>> think we allow anything special but hyphens in them, so I'm not
>>>> likely to see a lot of the possible corner cases.
>>>
>>> You can and it remembers. But it has no effect on what is sent ni
>>> the
>>> kerberos packets - what's sent there is what the user typed in.
>>> Yes, that
>>> sucks bad, but that's how it is.
>>
>> Hmmm.  See above.  It isn't possible to make it irrelevant
>> everywhere, unless you only use RC4.  Vista uses AES so it looses
>> that loophole though.
>>
> Yeah, I haven't tried Vista at all. I've only done 2000, XP and 2003.
>
>
>> I wonder if case conversion is the right way to create compatibility
>> with GSSAPI though.  If the user always comes in with a specific case
>> then shouldn't we instead find a way to make sure the PG user is
>> created with the corresponding case?  There are several ways you can
>> test for the existence of a user in a Kerberos service, for example
>> kinit with a garbage password will give different errors.
>
> That's why we have krb5_caseninsens_users - to let the user choose it.
>
>
> //Magnus
>

------------------------------------------------------------------------
The opinions expressed in this message are mine,
not those of Caltech, JPL, NASA, or the US Government.
Henry.B.Hotz@jpl.nasa.gov, or hbhotz@oxy.edu