def testInit(self): """Construct empty and seeded ShadowMapEntry.""" self.assertTrue(shadow.ShadowMapEntry(), msg='Could not create empty ShadowMapEntry') seed = {'name': 'foo'} entry = shadow.ShadowMapEntry(seed) self.assertTrue(entry.Verify(), msg='Could not verify seeded ShadowMapEntry') self.assertEqual(entry.name, 'foo', msg='Entry returned wrong value for name') self.assertEqual(entry.passwd, '!!', msg='Entry returned wrong value for passwd') self.assertEqual(entry.lstchg, None, msg='Entry returned wrong value for lstchg') self.assertEqual(entry.min, None, msg='Entry returned wrong value for min') self.assertEqual(entry.max, None, msg='Entry returned wrong value for max') self.assertEqual(entry.warn, None, msg='Entry returned wrong value for warn') self.assertEqual(entry.inact, None, msg='Entry returned wrong value for inact') self.assertEqual(entry.expire, None, msg='Entry returned wrong value for expire') self.assertEqual(entry.flag, None, msg='Entry returned wrong value for flag')
def testGetShadowMap(self): """Verify we build a correct shadow map from nss calls.""" line1 = 'foo:!!::::::::' line2 = 'bar:!!::::::::' lines = [line1, line2] mock_getent = self.mox.CreateMockAnything() mock_getent.communicate().AndReturn(['\n'.join(lines), '']) mock_getent.returncode = 0 entry1 = shadow.ShadowMapEntry() entry1.name = 'foo' entry2 = shadow.ShadowMapEntry() entry2.name = 'bar' self.mox.StubOutWithMock(nss, '_SpawnGetent') nss._SpawnGetent(config.MAP_SHADOW).AndReturn(mock_getent) self.mox.ReplayAll() shadow_map = nss.GetShadowMap() self.assertTrue(isinstance(shadow_map, shadow.ShadowMap)) self.assertEquals(len(shadow_map), 2) self.assertTrue(shadow_map.Exists(entry1)) self.assertTrue(shadow_map.Exists(entry2))
def testAttributes(self): """Test that we can get and set all expected attributes.""" entry = shadow.ShadowMapEntry() entry.name = 'foo' self.assertEqual(entry.name, 'foo', msg='Could not set attribute: name') entry.passwd = 'seekret' self.assertEqual(entry.passwd, 'seekret', msg='Could not set attribute: passwd') entry.lstchg = 0 self.assertEqual(entry.lstchg, 0, msg='Could not set attribute: lstchg') entry.min = 0 self.assertEqual(entry.min, 0, msg='Could not set attribute: min') entry.max = 0 self.assertEqual(entry.max, 0, msg='Could not set attribute: max') entry.warn = 0 self.assertEqual(entry.warn, 0, msg='Could not set attribute: warn') entry.inact = 0 self.assertEqual(entry.inact, 0, msg='Could not set attribute: inact') entry.expire = 0 self.assertEqual(entry.expire, 0, msg='Could not set attribute: expire') entry.flag = 0 self.assertEqual(entry.flag, 0, msg='Could not set attribute: flag')
def Transform(self, obj): """Transforms an LDAP shadowAccont object into a shadow(5) entry.""" shadow_ent = shadow.ShadowMapEntry() shadow_ent.name = obj['uid'][0] # TODO(jaq): does nss_ldap check the contents of the userPassword # attribute? shadow_ent.passwd = '*' if 'shadowLastChange' in obj: shadow_ent.lstchg = int(obj['shadowLastChange'][0]) if 'shadowMin' in obj: shadow_ent.min = int(obj['shadowMin'][0]) if 'shadowMax' in obj: shadow_ent.max = int(obj['shadowMax'][0]) if 'shadowWarning' in obj: shadow_ent.warn = int(obj['shadowWarning'][0]) if 'shadowInactive' in obj: shadow_ent.inact = int(obj['shadowInactive'][0]) if 'shadowExpire' in obj: shadow_ent.expire = int(obj['shadowExpire'][0]) if 'shadowFlag' in obj: shadow_ent.flag = int(obj['shadowFlag'][0]) if shadow_ent.flag is None: shadow_ent.flag = 0 if 'userPassword' in obj: passwd = obj['userPassword'][0] if passwd[:7].lower() == '{crypt}': shadow_ent.passwd = passwd[7:] else: logging.info('Ignored password that was not in crypt format') return shadow_ent
def ConvertValueToMapEntry(self, entry): """Convert a grent-like string into a ShadowMapEntry. Args: entry: A string containing a grent entry ala /etc/shadow Returns: A ShadowMapEntry instance """ if isinstance(entry, bytes): entry = entry.decode('ascii') elif entry.endswith('\x00'): entry = entry[:-1] entry = entry.split(':') map_entry = shadow.ShadowMapEntry() # map entries expect strict typing, so convert as appropriate map_entry.name = entry[0] map_entry.passwd = entry[1] if entry[2]: map_entry.lstchg = int(entry[2]) if entry[3]: map_entry.min = int(entry[3]) if entry[4]: map_entry.max = int(entry[4]) if entry[5]: map_entry.warn = int(entry[5]) if entry[6]: map_entry.inact = int(entry[6]) if entry[7]: map_entry.expire = int(entry[7]) if entry[8]: map_entry.flag = int(entry[8]) return map_entry
def testVerifyFailure(self): # create a map m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' self.assertTrue(m.Add(s)) updater = nssdb.NssDbShadowHandler({ 'dir': self.workdir, 'makedb': '/usr/bin/makedb' }) written = updater.Write(m) self.assertTrue(os.path.exists(updater.temp_cache_filename), 'updater.Write() did not create a file') # change the cache db = btopen(updater.temp_cache_filename) del db[db.first()[0]] db.sync() db.close() retval = updater.Verify(written) self.assertEqual(False, retval) self.assertFalse( os.path.exists(os.path.join(updater.temp_cache_filename)))
def setUp(self): self.good_entry = shadow.ShadowMapEntry() self.good_entry.name = 'foo' self.good_entry.passwd = '*' self.good_entry.lstchg = 17246 self.good_entry.min = 0 self.good_entry.max = 99999 self.good_entry.warn = 7 self.parser = s3source.S3ShadowMapParser()
def __init__(self, obj): """Set some default avalible data for testing.""" super(TestShadowMap, self).__init__(obj) self._good_entry = shadow.ShadowMapEntry() self._good_entry.name = 'foo' self._good_entry.lstchg = None self._good_entry.min = None self._good_entry.max = None self._good_entry.warn = None self._good_entry.inact = None self._good_entry.expire = None self._good_entry.flag = None
def testWriteShadowEntry(self): """We correctly write a typical entry in /etc/shadow format.""" cache = files.FilesShadowMapHandler(self.config) file_mock = self.mox.CreateMock(sys.stdout) file_mock.write('root:$1$zomgmd5support:::::::\n') map_entry = shadow.ShadowMapEntry() map_entry.name = 'root' map_entry.passwd = '$1$zomgmd5support' self.mox.ReplayAll() cache._WriteData(file_mock, map_entry)
def Transform(self, obj): """Transforms an LDAP shadowAccont object into a shadow(5) entry.""" shadow_ent = shadow.ShadowMapEntry() if self.conf.get('ad'): shadow_ent.name = obj['sAMAccountName'][0] elif 'uidattr' in self.conf: shadow_ent.name = obj[self.conf['uidattr']][0] else: shadow_ent.name = obj['uid'][0] if hasattr(self, 'uidregex'): shadow_ent.name = ''.join( [x for x in self.uidregex.findall(shadow_end.name)]) # TODO(jaq): does nss_ldap check the contents of the userPassword # attribute? shadow_ent.passwd = '*' if self.conf.get('ad'): # Time attributes of AD objects use interval date/time format with a value # that represents the number of 100-nanosecond intervals since January 1, 1601. # We need to calculate the difference between 1970-01-01 and 1601-01-01 in seconds wich is 11644473600 # then abstract it from the pwdLastChange value in seconds, then devide it by 86400 to get the # days since Jan 1, 1970 the password wa changed. shadow_ent.lstchg = int( (int(obj['pwdLastSet'][0]) / 10000000 - 11644473600) / 86400) elif 'shadowLastChange' in obj: shadow_ent.lstchg = int(obj['shadowLastChange'][0]) if 'shadowMin' in obj: shadow_ent.min = int(obj['shadowMin'][0]) if 'shadowMax' in obj: shadow_ent.max = int(obj['shadowMax'][0]) if 'shadowWarning' in obj: shadow_ent.warn = int(obj['shadowWarning'][0]) if 'shadowInactive' in obj: shadow_ent.inact = int(obj['shadowInactive'][0]) if 'shadowExpire' in obj: shadow_ent.expire = int(obj['shadowExpire'][0]) if 'shadowFlag' in obj: shadow_ent.flag = int(obj['shadowFlag'][0]) if shadow_ent.flag is None: shadow_ent.flag = 0 if 'userPassword' in obj: passwd = obj['userPassword'][0] if passwd[:7].lower() == '{crypt}': shadow_ent.passwd = passwd[7:] else: logging.info('Ignored password that was not in crypt format') return shadow_ent
def _ReadEntry(self, name, entry): """Return a ShadowMapEntry from a record in the target cache.""" map_entry = shadow.ShadowMapEntry() # maps expect strict typing, so convert to int as appropriate. map_entry.name = name map_entry.passwd = entry.get('passwd', '*') for attr in ['lstchg', 'min', 'max', 'warn', 'inact', 'expire']: try: setattr(map_entry, attr, int(entry[attr])) except (ValueError, KeyError): continue return map_entry
def testVerify(self): m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' self.failUnless(m.Add(s)) updater = nssdb.NssDbShadowHandler({ 'dir': self.workdir, 'makedb': '/usr/bin/makedb' }) written = updater.Write(m) self.failUnless(os.path.exists(updater.temp_cache_filename), 'updater.Write() did not create a file') retval = updater.Verify(written) self.failUnlessEqual(True, retval) os.unlink(updater.temp_cache_filename)
def testVerifyFailure(self): # Can't test if no makedb if not os.path.exists('/usr/bin/makedb'): raise TestSkipped('no /usr/bin/makedb') # Hide the warning that we expect to get class TestFilter(logging.Filter): def filter(self, record): return not record.msg.startswith( 'verify failed: %d keys missing') fltr = TestFilter() logging.getLogger('NssDbShadowHandler').addFilter(fltr) # create a map m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' self.failUnless(m.Add(s)) updater = nssdb.NssDbShadowHandler({ 'dir': self.workdir, 'makedb': '/usr/bin/makedb' }) written = updater.Write(m) self.failUnless(os.path.exists(updater.temp_cache_filename), 'updater.Write() did not create a file') # change the cache db = bsddb.btopen(updater.temp_cache_filename) del db[db.first()[0]] db.sync() db.close() retval = updater.Verify(written) self.failUnlessEqual(False, retval) self.failIf(os.path.exists(os.path.join(updater.temp_cache_filename))) # no longer hide this message logging.getLogger('NssDbShadowHandler').removeFilter(fltr)
def testNssDbShadowHandlerWrite(self): ent = 'foo:*:::::::0' makedb_stdin = self.mox.CreateMock(sys.stdin) makedb_stdin.write('.foo %s\n' % ent) makedb_stdin.write('00 %s\n' % ent) makedb_stdin.close() makedb_stdout = self.mox.CreateMock(sys.stdout) makedb_stdout.read().AndReturn('') makedb_stdout.close() m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' s.passwd = '*' s.Verify() self.failUnless(m.Add(s)) self.mox.StubOutWithMock(select, 'select') select.select([makedb_stdout], (), (), 0).AndReturn(([37], [], [])) select.select([makedb_stdout], (), (), 0).AndReturn(([], [], [])) def SpawnMakeDb(): makedb = MakeDbDummy() makedb.stdin = makedb_stdin makedb.stdout = makedb_stdout return makedb writer = nssdb.NssDbShadowHandler({ 'makedb': '/usr/bin/makedb', 'dir': self.workdir }) writer._SpawnMakeDb = SpawnMakeDb self.mox.ReplayAll() writer.Write(m) tmpshadow = os.path.join(self.workdir, 'shadow.db') self.failIf(os.path.exists(tmpshadow)) # just clean it up, Write() doesn't Commit() writer._Rollback()
def testNssDbShadowHandlerWriteData(self): ent = 'foo:!!:::::::0' makedb_stdin = self.mox.CreateMock(io.BytesIO) makedb_stdin.write(('.foo %s\n' % ent).encode('ascii')) makedb_stdin.write(('00 %s\n' % ent).encode('ascii')) m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' self.assertTrue(m.Add(s)) writer = nssdb.NssDbShadowHandler({ 'makedb': '/bin/false', 'dir': '/tmp' }) self.mox.ReplayAll() writer.WriteData(makedb_stdin, s, 0)
def testNssDbShadowHandlerWriteData(self): ent = 'foo:!!:::::::0' makedb_stdin = self.mox.CreateMock(sys.stdin) makedb_stdin.write('.foo %s\n' % ent) makedb_stdin.write('00 %s\n' % ent) m = shadow.ShadowMap() s = shadow.ShadowMapEntry() s.name = 'foo' self.failUnless(m.Add(s)) writer = nssdb.NssDbShadowHandler({ 'makedb': '/bin/false', 'dir': '/tmp' }) self.mox.ReplayAll() writer.WriteData(makedb_stdin, s, 0)
def GetShadowMap(): """Returns a ShadowMap built from nss calls.""" getent = _SpawnGetent(config.MAP_SHADOW) (getent_stdout, getent_stderr) = getent.communicate() # The following is going to be map-specific each time, so no point in # making more methods. shadow_map = shadow.ShadowMap() for line in getent_stdout.split(): line = line.decode('utf-8') nss_entry = line.strip().split(':') map_entry = shadow.ShadowMapEntry() map_entry.name = nss_entry[0] map_entry.passwd = nss_entry[1] if nss_entry[2] != '': map_entry.lstchg = int(nss_entry[2]) if nss_entry[3] != '': map_entry.min = int(nss_entry[3]) if nss_entry[4] != '': map_entry.max = int(nss_entry[4]) if nss_entry[5] != '': map_entry.warn = int(nss_entry[5]) if nss_entry[6] != '': map_entry.inact = int(nss_entry[6]) if nss_entry[7] != '': map_entry.expire = int(nss_entry[7]) if nss_entry[8] != '': map_entry.flag = int(nss_entry[8]) shadow_map.Add(map_entry) if getent_stderr: logging.debug('captured error %s', getent_stderr) retval = getent.returncode if retval != 0: logging.warning('%s returned error code: %d', GETENT, retval) return shadow_map
def _ReadEntry(self, line): """Return a ShadowMapEntry from a record in the target cache.""" line = line.split(':') map_entry = shadow.ShadowMapEntry() # map entries expect strict typing, so convert as appropriate map_entry.name = line[0] map_entry.passwd = line[1] if line[2]: map_entry.lstchg = int(line[2]) if line[3]: map_entry.min = int(line[3]) if line[4]: map_entry.max = int(line[4]) if line[5]: map_entry.warn = int(line[5]) if line[6]: map_entry.inact = int(line[6]) if line[7]: map_entry.expire = int(line[7]) if line[8]: map_entry.flag = int(line[8]) return map_entry
def testKey(self): """Key() should return the value of the 'name' attribute.""" entry = shadow.ShadowMapEntry() entry.name = 'foo' self.assertEqual(entry.Key(), entry.name)
def testVerify(self): """Test that the object can verify it's attributes and itself.""" entry = shadow.ShadowMapEntry() # Emtpy object should bomb self.assertFalse(entry.Verify())