1# Copyright 2007 Google Inc.
2#
3# This program is free software; you can redistribute it and/or
4# modify it under the terms of the GNU General Public License
5# as published by the Free Software Foundation; either version 2
6# of the License, or (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software Foundation,
15# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16"""NSS utility library."""
17
18__author__ = 'vasilios@google.com (Vasilios Hoffman)'
19
20import pwd
21import grp
22import logging
23import subprocess
24
25from nss_cache import config
26from nss_cache import error
27from nss_cache.maps import group
28from nss_cache.maps import passwd
29from nss_cache.maps import shadow
30
31# TODO(v): this should be a config option someday, but it's as standard
32# as libc so at the moment we'll leave it be for simplicity.
33GETENT = '/usr/bin/getent'
34
35
36def GetMap(map_name):
37    """Retrieves a Map of type map_name via nss calls."""
38
39    if map_name == config.MAP_PASSWORD:
40        return GetPasswdMap()
41    elif map_name == config.MAP_GROUP:
42        return GetGroupMap()
43    elif map_name == config.MAP_SHADOW:
44        return GetShadowMap()
45
46    raise error.UnsupportedMap
47
48
49def GetPasswdMap():
50    """Returns a PasswdMap built from nss calls."""
51    passwd_map = passwd.PasswdMap()
52
53    for nss_entry in pwd.getpwall():
54        map_entry = passwd.PasswdMapEntry()
55        map_entry.name = nss_entry[0]
56        map_entry.passwd = nss_entry[1]
57        map_entry.uid = nss_entry[2]
58        map_entry.gid = nss_entry[3]
59        map_entry.gecos = nss_entry[4]
60        map_entry.dir = nss_entry[5]
61        map_entry.shell = nss_entry[6]
62        passwd_map.Add(map_entry)
63
64    return passwd_map
65
66
67def GetGroupMap():
68    """Returns a GroupMap built from nss calls."""
69    group_map = group.GroupMap()
70
71    for nss_entry in grp.getgrall():
72        map_entry = group.GroupMapEntry()
73        map_entry.name = nss_entry[0]
74        map_entry.passwd = nss_entry[1]
75        map_entry.gid = nss_entry[2]
76        map_entry.members = nss_entry[3]
77        if not map_entry.members:
78            map_entry.members = ['']
79        group_map.Add(map_entry)
80
81    return group_map
82
83
84def GetShadowMap():
85    """Returns a ShadowMap built from nss calls."""
86    getent = _SpawnGetent(config.MAP_SHADOW)
87    (getent_stdout, getent_stderr) = getent.communicate()
88
89    # The following is going to be map-specific each time, so no point in
90    # making more methods.
91    shadow_map = shadow.ShadowMap()
92
93    for line in getent_stdout.split():
94        line = line.decode('utf-8')
95        nss_entry = line.strip().split(':')
96        map_entry = shadow.ShadowMapEntry()
97        map_entry.name = nss_entry[0]
98        map_entry.passwd = nss_entry[1]
99        if nss_entry[2] != '':
100            map_entry.lstchg = int(nss_entry[2])
101        if nss_entry[3] != '':
102            map_entry.min = int(nss_entry[3])
103        if nss_entry[4] != '':
104            map_entry.max = int(nss_entry[4])
105        if nss_entry[5] != '':
106            map_entry.warn = int(nss_entry[5])
107        if nss_entry[6] != '':
108            map_entry.inact = int(nss_entry[6])
109        if nss_entry[7] != '':
110            map_entry.expire = int(nss_entry[7])
111        if nss_entry[8] != '':
112            map_entry.flag = int(nss_entry[8])
113        shadow_map.Add(map_entry)
114
115    if getent_stderr:
116        logging.debug('captured error %s', getent_stderr)
117
118    retval = getent.returncode
119
120    if retval != 0:
121        logging.warning('%s returned error code: %d', GETENT, retval)
122
123    return shadow_map
124
125
126def _SpawnGetent(map_name):
127    """Run 'getent map' in a subprocess for reading NSS data."""
128    getent = subprocess.Popen([GETENT, map_name],
129                              stdout=subprocess.PIPE,
130                              stderr=subprocess.PIPE)
131
132    return getent
133