This file is indexed.

/usr/lib/ruby/vendor_ruby/merb-core/dispatch/session/cookie.rb is in ruby-merb-core 1.1.3+dfsg-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
require 'base64'        # to convert Marshal.dump to ASCII
require 'openssl'       # to generate the HMAC message digest
module Merb
  
  # If you have more than 4K of session data or don't want your data to be
  # visible to the user, pick another session store.
  #
  # CookieOverflow is raised if you attempt to store more than 4K of data.
  # TamperedWithCookie is raised if the data integrity check fails.
  #
  # A message digest is included with the cookie to ensure data integrity:
  # a user cannot alter session data without knowing the secret key included
  # in the hash.
  #
  # To use Cookie Sessions, set in config/merb.yml
  #  :session_secret_key - your secret digest key
  #  :session_store - cookie
  class CookieSession < SessionContainer
    # TODO (maybe):
    # include request ip address
    # AES encrypt marshaled data
    
    # Raised when storing more than 4K of session data.
    class CookieOverflow < StandardError; end
    
    # Raised when the cookie fails its integrity check.
    class TamperedWithCookie < StandardError; end
    
    # Cookies can typically store 4096 bytes.
    MAX = 4096
    DIGEST = OpenSSL::Digest::Digest.new('SHA1') # or MD5, RIPEMD160, SHA256?
    
    # :api: private
    attr_accessor :_original_session_data
    
    # The session store type
    self.session_store_type = :cookie
    
    class << self
      # Generates a new session ID and creates a new session.
      #
      # ==== Returns
      # SessionContainer:: The new session.
      # 
      # :api: private
      def generate
        self.new(Merb::SessionMixin.rand_uuid, "", Merb::Request._session_secret_key)
      end
      
      # Set up a new session on request: make it available on request instance.
      #
      # ==== Parameters
      # request<Merb::Request>:: The Merb::Request that came in from Rack.
      #
      # ==== Returns
      # SessionContainer:: a SessionContainer. If no sessions were found,
      # a new SessionContainer will be generated.
      # 
      # :api: private
      def setup(request)
        session = self.new(Merb::SessionMixin.rand_uuid,
          request.session_cookie_value, request._session_secret_key)
        session._original_session_data = session.to_cookie
        request.session = session
      end
      
    end
    
    # ==== Parameters
    # session_id<String>:: A unique identifier for this session.
    # cookie<String>:: The raw cookie data.
    # secret<String>:: A session secret.
    #
    # ==== Raises
    # ArgumentError:: blank or insufficiently long secret.
    # 
    # :api: private
    def initialize(session_id, cookie, secret)
      super session_id
      if secret.blank? || secret.length < 16
        msg = "You must specify a session_secret_key in your init file, and it must be at least 16 characters"
        Merb.logger.warn(msg)
        raise ArgumentError, msg
      end
      @secret = secret
      self.update(unmarshal(cookie))
    end
    
    # Teardown and/or persist the current session.
    #
    # If @_destroy is true, clear out the session completely, including
    # removal of the session cookie itself.
    #
    # ==== Parameters
    # request<Merb::Request>:: request object created from Rack environment.
    # 
    # :api: private
    def finalize(request)
      if @_destroy
        request.destroy_session_cookie
      elsif _original_session_data != (new_session_data = self.to_cookie)
        request.set_session_cookie_value(new_session_data)
      end
    end
    
    # Regenerate the session_id.
    # 
    # :api: private
    def regenerate
      self.session_id = Merb::SessionMixin.rand_uuid
    end
    
    # Create the raw cookie string; includes an HMAC keyed message digest.
    #
    # ==== Returns
    # String:: Cookie value.
    #
    # ==== Raises
    # CookieOverflow:: More than 4K of data put into session.
    #
    # ==== Notes
    # Session data is converted to a Hash first, since a container might
    # choose to marshal it, which would make it persist
    # attributes like 'needs_new_cookie', which it shouldn't.
    # 
    # :api: private
    def to_cookie
      unless self.empty?
        data = self.serialize
        value = Merb::Parse.escape "#{data}--#{generate_digest(data)}"
        if value.size > MAX
          msg = "Cookies have limit of 4K. Session contents: #{data.inspect}"
          Merb.logger.error!(msg)
          raise CookieOverflow, msg
        end
        value
      end
    end
    
    private
    
    # Generate the HMAC keyed message digest. Uses SHA1.
    # 
    # ==== Returns
    # String:: an HMAC digest of the cookie data.
    # 
    # :api: private
    def generate_digest(data)
      OpenSSL::HMAC.hexdigest(DIGEST, @secret, data)
    end

    # Securely compare two digests using a constant time algorithm.
    # This avoids leaking information about the calculated HMAC
    #
    # Based on code by Michael Koziarski <michael@koziarski.com>
    # http://github.com/rails/rails/commit/674f780d59a5a7ec0301755d43a7b277a3ad2978
    #
    # ==== Parameters
    # a, b<~to_s>:: digests to compare.
    #
    # ==== Returns
    # Boolean:: Do the digests validate?
    def secure_compare(a, b)
      if a.length == b.length

        # unpack to forty characters.
        # needed for 1.8 and 1.9 compat
        a_bytes = a.unpack('C*')
        b_bytes = b.unpack('C*')

        result = 0
        for i in 0..(a_bytes.length - 1)
          result |= a_bytes[i] ^ b_bytes[i]
        end
        result == 0
      else
        false
      end
    end

    
    # Unmarshal cookie data to a hash and verify its integrity.
    # 
    # ==== Parameters
    # cookie<~to_s>:: The cookie to unmarshal.
    # 
    # ==== Raises
    # TamperedWithCookie:: The digests don't match.
    # 
    # ==== Returns
    # Hash:: The stored session data.
    # 
    # :api: private
    def unmarshal(cookie)
      if cookie.blank?
        {}
      else
        data, digest = Merb::Parse.unescape(cookie).split('--')
        return {} if data.blank? || digest.blank?
        unless secure_compare(generate_digest(data), digest)
          clear
          unless Merb::Config[:ignore_tampered_cookies]
            raise TamperedWithCookie, "Maybe the site's session_secret_key has changed?"
          end
        end
        unserialize(data)
      end
    end
    
    protected
    
    # Serialize current session data as a Hash.
    # Uses Base64 encoding for integrity.
    # 
    # ==== Returns
    # String:: Base64 encoded dump of the session hash.
    # 
    # :api: private
    def serialize
      Base64.encode64(Marshal.dump(self.to_hash)).chop
    end
    
    # Unserialize the raw cookie data to a Hash
    # 
    # ==== Returns
    # Hash:: the session hash Base64 decoded from the data dump.
    # 
    # :api: private
    def unserialize(data)
      Marshal.load(Base64.decode64(data)) rescue {}
    end
  end
end