PreviousUpNext

15.4.1599  src/lib/x-kit/xclient/src/stuff/authentication.pkg

## authentication.pkg
#
# For motivation and overview see comments in:
#
#     src/lib/x-kit/xclient/xclient.api
#
# Support for X11 authentication.  The authentication file, which is
# specified by the XAUTHORITY variable (default $HOME/.Xauthority),
# consists of a sequence of entries with the following format:
#
#      2 bytes         Family value (second byte is as in protocol HOST)
#      2 bytes         address length (always MSB first)
#      A bytes         host address (as in protocol HOST)
#      2 bytes         display "number" length (always MSB first)
#      S bytes         display "number" string
#      2 bytes         name length (always MSB first)
#      N bytes         authorization name string
#      2 bytes         data length (always MSB first)
#      D bytes         authorization data string
#
# For more information see the README in the libxau sourcetree from X.org.
#
# This implementation is partially based on code contributed by Juergen Buntrock.

# Compiled by:
#     src/lib/x-kit/xclient/xclient-internals.sublib

# This package is (only) used in:
#
#     src/lib/x-kit/widget/old/lib/run-in-x-window-old.pkg



###            "Adam and Eve had many advantages, but the
###             principal one was that they escaped teething."
###
###                            -- Pudd'nhead Wilson's Calendar



stipulate
    package xt  =  xtypes;                                                              # xtypes                        is from   src/lib/x-kit/xclient/src/wire/xtypes.pkg
    package bio =  data_file__premicrothread;                                           # data_file__premicrothread     is from   src/lib/std/src/posix/data-file--premicrothread.pkg
    package ss  =  substring;                                                           # substring                     is from   src/lib/std/substring.pkg
    package dns =  dns_host_lookup;                                                     # dns_host_lookup               is from   src/lib/std/src/socket/dns-host-lookup.pkg
    package cxa =  crack_xserver_address;                                               # crack_xserver_address         is from   src/lib/x-kit/xclient/src/wire/crack-xserver-address.pkg
herein


    package authentication {
        #
        w8vextract = vector_slice_of_one_byte_unts::to_vector  o  vector_slice_of_one_byte_unts::make_slice;

        get8 = one_byte_unt::to_int o vector_of_one_byte_unts::get;

        # This version of get16 handles unaligned data:
        #
        fun get16 (s, i)
            =
            {   s =   w8vextract (s, i, THE 2);
                #
                large_unt::to_int (pack_big_endian_unt16::get_vec (s, 0));
            };

        fun get_data (s, i, n)
            =
            w8vextract (s, i, THE n);

        # Extract n bytes at offset i within s,
        # return as a String:
        #
        fun get_string (s, i, n)
            =
            byte::unpack_string_vector (vector_slice_of_one_byte_unts::make_slice (s, i, THE n));


        # Extract 4-byte IP address from offset 'i'
        # in string 's' and return as a "127.0.0.1"
        # style dotted ascii String:
        #
        fun get_address_string (s, i, n)
            =
            {   address_parts
                    = 
                    (vector_slice_of_one_byte_unts::fold_backward
                        (\\ (e, a)
                            = 
                            (int::to_string (char::to_int (byte::byte_to_char e))) ! a
                        )
                        []
                        (vector_slice_of_one_byte_unts::make_slice (s, i, THE n))
                    );

                case (n, address_parts)
                    #
                    (4, [a, b, c, d]) =>   a + "." + b + "." + c + "." + d;
                    _                 =>   "";                                  # XXX BUGGO FIXME This can't be optimal.
                esac;
            };

        # The different family codes
        # (from X.h and xc/lib/Xau/Xauth.h)
        #
        family_internet =  0;
        family_decnet   =  1;
        family_chaos    =  2;
        family_local    =  256;
        family_wild     =  65535;

        # Return the default name of the authentication file (either
        # specified by the XAUTHORITY environment variable, or the
        # file $HOME/.Xauthority.  If neither XAUTHORITY nor HOME 
        # are defined, then ".Xauthority" is returned.
        #
        fun get_xauthority_filename ()
            =
            case (winix::process::get_env "XAUTHORITY")
                #
                THE filename => filename;
                #
                NULL
                    =>
                    case (winix::process::get_env "HOME")
                        #
                        THE path =>  path + "/.Xauthority";
                        NULL     =>  ".Xauthority";
                    esac;
            esac;


        # Read the entire contents of a .Xauthority file:
        # 
        fun read_file filename
            = 
            {   stream   =   data_file__premicrothread::open_for_read  filename;
                #
                contents =   data_file__premicrothread::read_all  stream;

                data_file__premicrothread::close_input  stream;

                contents;
            };

        # Extract an authentication entry from
        # given offset in given file_contents.
        #
        # Our effective type here is
        #
        #     file_contents -> offset -> entry
        #
        # where file_contents is a bytestring (vector_of_one_byte_unts)
        # holding the contents of a ~/.Xauathority file or
        # such and 'offset' is an Int giving a byte offset
        # within file_contents.
        #
        fun extract_authentication_entry  file_contents
            =
            {   len =   vector_of_one_byte_unts::length  file_contents;

                fun get_len start
                    =
                    get16 (file_contents, start - 2);

                # Extract one .Xauthority file record from
                # given offset within 'file_contents' bytestring:
                #
                fun extract offset
                    =
                    if (offset < len)
                        #
                        addr_start =   4 + offset;
                        addr_len   =   get_len addr_start;

                        display_start  =   addr_start + addr_len + 2;
                        display_len    =   get_len display_start;

                        name_start =   display_start + display_len + 2;
                        name_len   =   get_len name_start;

                        data_start =   name_start + name_len + 2;
                        data_len   =   get_len data_start;

                        next       =   data_start + data_len;

                        #  Added following line, Feb 2005, ddeboer 
                        family = get16 (file_contents, offset);

                        # modified by ddeboer: 
                        # entries of family=familyInternet are stored as 4-byte ip addresses.
                        # it seems that we must convert these into hostnames for comparison...?!?
                        # original:
                        # family = get16 (file_contents, offset),
                        # address = get_string (file_contents, addrStart, addrLen),
                        #
                        address
                            =
                            if (family == family_internet)

                                string =   get_address_string (file_contents, addr_start, addr_len);

                                # For 110.59, Dusty Deboer replaced the below by just
                                #
                                string;

#                               string =   get_address_string (file_contents, addr_start, addr_len);
#
#                               case (dns_host_lookup::from_string string)   
#
#                                   NULL   => "";
#
#                                   THE ia => case (dns_host_lookup::get_by_address ia)    
#                                                 NULL  => ""; 
#                                                 THE e => (dns_host_lookup::name e);
#                                             esac;
#                               esac;

                            else
                                get_string (file_contents, addr_start, addr_len);
                            fi;

                            #  end modification 


                        THE (
                            xt::XAUTHENTICATION {
                                 family,
                                 address,
                                 display =>  get_string (file_contents, display_start,  display_len ),
                                 name    =>  get_string (file_contents, name_start,     name_len),
                                 data    =>  get_data   (file_contents, data_start,     data_len)
                            },
                            next
                        );
                    else
                        NULL;
                    fi;

                extract;
            };

        # Search the default authentication file for the first entry that
        # matches the family, network address and display number.
        #
        # If no such match is found, return NULL.
        #
        # The * value family_wild matches anything, as do the
        # empty strings when given for address or display.
        #
        fun get_xauthority_file_entry_by_address
            {
              family:    Int,           # family_wild, family_local, family_internet ...
              address:   String,        # From gethostname(2) or such.
              display:   String         # E.g. "0" -- from "localhost:0.1" DISPLAY string or such.
            }
            = 
            {   extract_authentication_entry
                    =
                    extract_authentication_entry (read_file (get_xauthority_filename()));

                # hack by ddeboer, Feb 2005 - this is surely not the right way to do this...??  XXX BUGGO FIXME
                # if family is internet and address is local_host, change to the local hostname
                # and family_local.
                #
    #       my (family, address)
    #                =
    #                if (address == "localhost" and
    #               family  == family_internet
    #           )
    #                (family_local, dns_host_lookup::get_host_name());
    #           else
    #                     (family, address);
    #                fi; 

                #  end hack 

                fun strings_match ("", _)   =>   TRUE;
                    strings_match (_, "")   =>   TRUE;
                    strings_match (a, b)    =>   (a == b);
                end;

                fun entry_is_acceptable (xt::XAUTHENTICATION { family=>f, display=>d, address=>a, ... } )
                    =
                    (   # tracing added ddeboer, Jan 2005. 
                        #  (file::print ("chkAuth seeking family=" + (int::to_string (family)) + ", display="
                        #    + display + ", address=" + address + "; examining address=" + a + ", display=" + d + "\n"));

                        (   (family == family_wild)   or
                            (f      == family_wild)   or
                            (family == f)
                        )
                        and strings_match (display, d)
                        and strings_match (address, a)
                    );

                fun get_entry  offset
                    =
                    case (extract_authentication_entry offset)
                        #
                        THE (entry, next_offset)
                            =>
                            if (entry_is_acceptable  entry)   THE entry;
                            else                              get_entry  next_offset;
                            fi;

                        NULL => NULL;
                    esac;


                get_entry 0;
            }
            except
                _ = NULL;

        # This is similar to get_xauthority_file_entry_by_address,
        # except that a list of acceptable authentication methods
        # is specified by the list acceptable_authentication_methods.
        # This contains one or more strings like
        #
        #     "MIT-MAGIC-COOKIE-1"
        #     "XDM-AUTHORIZATION-1"
        #     "SUN-DES-1"
        #     "MIT-KERBEROS-5"
        #
        # to match literally against the contents of ~/.Xauthority entries.
        #
        # Not all of these are available everywhere; the de facto standard
        # method is MIT-MAGIC-COOKIE-1.  For more information about the
        # various authentication methods see (e.g.):
        #
        #     man 7 Xsecurity
        #     http://manpages.ubuntu.com/manpages/jaunty/man7/Xsecurity.7.html
        #
        # We return the matching authentication info that matches the earliest
        # name on the list.
        #
        # We return NULL if no match is found.
        #
        fun get_best_xauthority_file_entry_by_address
            {
              family:    Int,                   # family_wild, family_local, family_internet ...
              address:   String,                # From gethostname(2) or such.
              display:   String,                # E.g. "0" -- from "localhost:0.1" DISPLAY string or such.
              # 
              acceptable_authentication_methods: List(String)   # E.g.  [ "MIT-MAGIC-COOKIE-1" ]
            }
            =
            {   extract_authentication_entry =   extract_authentication_entry (read_file (get_xauthority_filename()));

                #  hack by ddeboer, Feb 2005 - this is surely not the right way to do this...?? 
                #  if family is internet and address is localhost, change to the local hostname
                #  and familyLocal.

    #       my (family, address)
    #                =
    #                if (address == "localhost" and       #  or (address=="")
    #               family  == family_internet
    #           ) 
    #                (family_local, dns_host_lookup::get_host_name());
    #           else (family, address);
    #                fi;

                #  end hack 

                fun strings_match ("", _) =>   TRUE;
                    strings_match (_, "") =>   TRUE;
                    strings_match (a,  b) =>   (a == b);
                end;

                fun check_auth (xt::XAUTHENTICATION { family=>f, display=>d, address=>a, ... } )
                    =
                    (   (   family == family_wild   or
                            f      == family_wild   or
                            family == f
                        )
                        and strings_match (display, d)
                        and strings_match (address, a)
                    );

                fun get (offset, best_rank, best)
                    =
                    case (extract_authentication_entry offset)
                        #
                        NULL => best;
                        #
                        THE (auth as xt::XAUTHENTICATION { name, ... }, next)
                             =>
                             if (check_auth auth)

                                 fun check_name (   [],    _)
                                         =>
                                         get (next, best_rank, best);

                                     check_name (n ! r, rank)
                                         =>
                                         if (rank < best_rank)

                                                if (name == n)   get (next, rank, THE auth);
                                                else             check_name (r, rank+1);
                                                fi;

                                         else   get (next, best_rank, best);
                                         fi;
                                 end;

                                 check_name (acceptable_authentication_methods, 0);

                             else
                                 get (next, best_rank, best);
                             fi;
                    esac;


                get (0, length acceptable_authentication_methods, NULL);
            }
            except _ = NULL;


        # Read the specified authentication file
        # and return a list of the entries that
        # satisfy the given predicate.
        #
        fun get_selected_xauthority_file_entries  want_entry  file
            =
            filter (0, [])
            where
                extract_authentication_entry =   extract_authentication_entry (read_file file);

                fun filter (offset, results_so_far)
                    =
                    case (extract_authentication_entry  offset)
                        #
                        NULL => reverse results_so_far;
                        #
                        THE (this_entry, next_offset)
                             =>
                             want_entry  this_entry
                                 ##     
                                 ??  filter (next_offset, this_entry ! results_so_far)
                                 ::  filter (next_offset,              results_so_far);
                    esac;
            end;


        fun get_display_name NULL
                =>
                case (winix::process::get_env  "DISPLAY")
                    #
                    THE display =>  display;
                    NULL        =>  "";
                esac;

            get_display_name (THE display)
                =>
                display;
        end;


        # Parse an xdisplay string:
        #
        #     "foo.com:0.0"
        #     ->
        #     { host    => "foo.com",
        #       display => "0",
        #       screen  => "0"
        #     }
        #
        fun parse_xdisplay_string ""
                =>
                { host=>"", display=>"0", screen=>"0"};

            parse_xdisplay_string d
                =>
                {   my (host, rest)
                        =
                        ss::split_off_prefix  {. #c != ':'; }   (ss::from_string d);

                    my (display, screen)
                        =
                        ss::split_off_prefix   {. #c != '.'; }  rest;

                    if (ss::size display < 2)
                        #
                        raise exception cxa::XSERVER_CONNECT_ERROR "No display field";
                    else
                        if (ss::size screen == 1)
                            #
                            raise exception cxa::XSERVER_CONNECT_ERROR "No screen number";
                        else
                            { host    =>   ss::to_string host,
                              display =>   ss::to_string (ss::drop_first 1 display),
                              screen  =>   ss::to_string (ss::drop_first 1 screen)
                            };
                        fi;
                    fi;
                };
        end;



        # Given an optional display name, return the
        # display and authentication information.
        #
        # If the argument is NULL, then we use the
        # DISPLAY unix environment variable if any
        # else "".
        #
        fun get_xdisplay_string_and_xauthentication  display_option
            = 
            {   display =   get_display_name  display_option;

                xauthentication
                    =
                    case display
                        #                        
                        "" =>   get_xauthority_file_entry_by_address
                                  {
                                    family  =>  family_wild,
                                    address =>  dns::get_host_name (),  # Necessary to look up xrdb record -- ddeboer, 110.59.
                                    display =>  "0"
                                  };

                        d => {  my { host, display, ... }
                                    =
                                    parse_xdisplay_string d;


                                fun make_xa  family  address                            # "xa" may be "x-window authentication (string)"
                                    =
                                    get_xauthority_file_entry_by_address { family, address, display };


                                # We must obtain the string to be used for comparison
                                # in get_xauthority_file_entry_by_address.
                                # For family_local this is the local hostname.
                                # For family_internet, this is the IP address as a string: "128.74.13.14" or such.
                                #     -- ddeboer, 110.59
                                #       
                                case host
                                    #
                                    ("" | "localhost")
                                        =>
                                        make_xa   family_local   (dns::get_host_name ());

                                    _   =>
                                        {   address
                                                =
                                                # This should more properly be set to the
                                                # peer address of the connection, *after*
                                                # the connection has been made.  However,
                                                # that would be a bit difficult with this
                                                # architecture. -- ddeboer 110.59
                                                #
                                                case (dns::get_by_name  host)
                                                    #
                                                    THE entry =>  dns::to_string (dns::address  entry);
                                                    NULL      =>  "";
                                                esac;

                                            make_xa  family_internet  address;
                                        };
                                esac;
                             };
                     esac;


                (display, xauthentication);
            };


    };                          # package xauth
end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext