This file is indexed.

/usr/lib/python2.7/dist-packages/nss_cache/caches/caches.py is in nsscache 0.33-3.

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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# Copyright 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

"""Base class of cache for nsscache."""

__author__ = 'jaq@google.com (Jamie Wilkinson)'

import errno
import logging
import os
import shutil
import stat
import tempfile

from nss_cache import config
from nss_cache import error
from nss_cache.maps import automount
from nss_cache.maps import group
from nss_cache.maps import netgroup
from nss_cache.maps import passwd
from nss_cache.maps import shadow
from nss_cache.maps import sshkey

class Cache(object):
  """Abstract base class for Caches.

  The Cache object represents the cache used by NSS, that we plan on
  writing the NSS data to -- it is the cache that we up date so that
  the NSS module has a place to retrieve data from.  Typically a cache
  is some form of on-disk local storage.

  You can manipulate a cache directly, like asking for a Map object from
  it, or giving it a Map to write out to disk.  There is an Updater class
  which holds the logic for taking data from Source objects and merging them
  with Cache objects.

  It is important to note that a new Cache is instantiated for each
  'map' defined in the configuration -- allowing different Cache
  storages for different NSS maps, instead of one Cache to hold them all
  (and in the darkness bind them).
  """

  def __init__(self, conf, map_name, automount_mountpoint=None):
    """Initialise the Cache object.

    Args:
      conf: A dictionary of key/value pairs
      map_name: A string representation of the map type
      automount_mountpoint: A string containing the automount mountpoint,
        used only by automount maps.

    Raises:
      UnsupportedMap: for map types we don't know about
    """
    super(Cache, self).__init__()
    # Set up a logger for our children
    self.log = logging.getLogger(self.__class__.__name__)
    # Store config info
    self.conf = conf
    self.output_dir = conf.get('dir', '.')
    self.automount_mountpoint = automount_mountpoint
    self.map_name = map_name

    # Setup the map we may be asked to load our cache into.
    if map_name == config.MAP_PASSWORD:
      self.data = passwd.PasswdMap()
    elif map_name == config.MAP_SSHKEY:
      self.data = sshkey.SshkeyMap()
    elif map_name == config.MAP_GROUP:
      self.data = group.GroupMap()
    elif map_name == config.MAP_SHADOW:
      self.data = shadow.ShadowMap()
    elif map_name == config.MAP_NETGROUP:
      self.data = netgroup.NetgroupMap()
    elif map_name == config.MAP_AUTOMOUNT:
      self.data = automount.AutomountMap()
    else:
      raise error.UnsupportedMap('Cache does not support %s' % map_name)

  def _Begin(self):
    """Start a write transaction."""
    self.log.debug('Output dir: %s', self.output_dir)
    self.log.debug('CWD: %s', os.getcwd())
    try:
      (fd, self.temp_cache_filename) = tempfile.mkstemp(
          prefix='nsscache-cache-file-',
          dir=os.path.join(os.getcwd(), self.output_dir))
      self.temp_cache_file = os.fdopen(fd, 'w+b')
      self.log.debug('opened temporary cache filename %r',
                     self.temp_cache_filename)
    except OSError, e:
      if e.errno == errno.EACCES:
        self.log.info('Got OSError (%s) when trying to create temporary file',
                      e)
        raise error.PermissionDenied('OSError: ' + str(e))
      raise

  def _Rollback(self):
    """Rollback a write transaction."""
    self.log.debug('rolling back, deleting temp cache file %r',
                   self.temp_cache_filename)
    self.temp_cache_file.close()
    # Safe file remove (ignore "no such file or directory" errors):
    try:
      os.remove(self.temp_cache_filename)
    except OSError, e:
      if e.errno != errno.ENOENT:  # errno.ENOENT = no such file or directory
        raise  # re-raise exception if a different error occured

  def _Commit(self):
    """Ensure the cache is now the active data source for NSS.

    Perform an atomic rename on the cache file to the location
    expected by the NSS module.  No verification of database validity
    or consistency is performed here.

    Returns:
      Always returns True
    """
    # TODO(jaq): if self WriteModifyTimestamp() fails below, we still have a
    # new cache, but we might instead want to reserve the space on
    # disk for a timestamp first -- thus needing a write/commit pair
    # of functions for a timestamp.  Edge case, so not bothering for now.
    if not self.temp_cache_file.closed:
      self.temp_cache_file.flush()
      os.fsync(self.temp_cache_file.fileno())
      self.temp_cache_file.close()
    else:
      self.log.debug('temp cache file was already closed before Commit')
    # We emulate the permissions of our source map to avoid bugs where
    # permissions may differ (usually w/shadow map)
    # Catch the case where the source file may not exist for some reason and
    # chose a sensible default.
    try:
      shutil.copymode(self.GetCompatFilename(), self.temp_cache_filename)
      stat_info = os.stat(self.GetCompatFilename())
      uid = stat_info.st_uid
      gid = stat_info.st_gid
      os.chown(self.temp_cache_filename, uid, gid)
    except OSError, e:
      if e.errno == errno.ENOENT:
        if self.map_name == "sshkey":
          os.chmod(self.temp_cache_filename,
                   stat.S_IRUSR|stat.S_IRGRP|stat.S_IROTH)
        else:
          os.chmod(self.temp_cache_filename,
                   stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IROTH)
    self.log.debug('committing temporary cache file %r to %r',
                   self.temp_cache_filename, self.GetCacheFilename())
    os.rename(self.temp_cache_filename, self.GetCacheFilename())
    return True

  def GetCacheFilename(self):
    """Return the final destination pathname of the cache file."""
    return os.path.join(self.output_dir, self.CACHE_FILENAME)

  def GetCompatFilename(self):
    """Return the filename where the normal (not-cache) map would be."""
    # TODO(jaq): Probably shouldn't hard code '/etc' here.
    return os.path.join('/etc', self.map_name)

  def GetMap(self, cache_filename=None):
    """Returns the map from the cache.

    Must be implemented by the child class!

    Args:
      cache_filename:  optional extra info used by the child class

    Raises:
      NotImplementedError:  We should have been implemented by child.
    """
    raise NotImplementedError('%s must implement this method!' %
                              self.__class__.__name__)

  def GetMapLocation(self):
    """Return the location of the Map in this cache.

    This is used by automount maps so far, and must be implemented in the
    child class only if it is to support automount maps.

    Raises:
      NotImplementedError:  We should have been implemented by child.
    """
    raise NotImplementedError('%s must implement this method!' %
                              self.__class__.__name__)

  def WriteMap(self, map_data=None, force_write=False):
    """Write a map to disk.

    Args:
      map_data: optional Map object to overwrite our current data with.
      force_write: optional flag to indicate verification checks can be
        ignored.

    Returns:
      0 if succesful, 1 if not
    """
    if map_data is None:
      writable_map = self.data
    else:
      writable_map = map_data

    entries_written = self.Write(writable_map)

    # N.B. Write is destructive, len(writable_map) == 0 now.
    # Asserting this isn't good for the unit tests, though.
    #assert 0 == len(writable_map), "self.Write should be destructive."

    if entries_written is None:
      self.log.warn('cache write failed, exiting')
      return 1

    if force_write or self.Verify(entries_written):
      # TODO(jaq): in the future we should handle return codes from
      # Commit()
      self._Commit()
      # Create an index for this map.
      self.WriteIndex()
      return 0

    self.log.warn('verification failed, exiting')
    return 1

  def WriteIndex(self):
    """Build an index for this cache.

    No-op, but child classes may override this.
    """
    pass

  def Write(self, writable_map):
    raise NotImplementedError

  def Verify(self, entries_written):
    raise NotImplementedError