Blame utils/nslcd.py

Packit 6bd9ab
# coding: utf-8
Packit 6bd9ab
Packit 6bd9ab
# nslcd.py - functions for doing nslcd requests
Packit 6bd9ab
#
Packit 6bd9ab
# Copyright (C) 2013-2017 Arthur de Jong
Packit 6bd9ab
#
Packit 6bd9ab
# This library is free software; you can redistribute it and/or
Packit 6bd9ab
# modify it under the terms of the GNU Lesser General Public
Packit 6bd9ab
# License as published by the Free Software Foundation; either
Packit 6bd9ab
# version 2.1 of the License, or (at your option) any later version.
Packit 6bd9ab
#
Packit 6bd9ab
# This library is distributed in the hope that it will be useful,
Packit 6bd9ab
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6bd9ab
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6bd9ab
# Lesser General Public License for more details.
Packit 6bd9ab
#
Packit 6bd9ab
# You should have received a copy of the GNU Lesser General Public
Packit 6bd9ab
# License along with this library; if not, write to the Free Software
Packit 6bd9ab
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6bd9ab
# 02110-1301 USA
Packit 6bd9ab
Packit 6bd9ab
import fcntl
Packit 6bd9ab
import os
Packit 6bd9ab
import socket
Packit 6bd9ab
import struct
Packit 6bd9ab
import sys
Packit 6bd9ab
Packit 6bd9ab
import constants
Packit 6bd9ab
Packit 6bd9ab
Packit 6bd9ab
# definition for reading and writing INT32 values
Packit 6bd9ab
_int32 = struct.Struct('!i')
Packit 6bd9ab
Packit 6bd9ab
Packit 6bd9ab
class NslcdClient(object):
Packit 6bd9ab
Packit 6bd9ab
    def __init__(self, action):
Packit 6bd9ab
        # set up the socket (store in class to avoid closing it)
Packit 6bd9ab
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Packit 6bd9ab
        fcntl.fcntl(self.sock, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
Packit 6bd9ab
        # connect to nslcd
Packit 6bd9ab
        self.sock.connect(constants.NSLCD_SOCKET)
Packit 6bd9ab
        #self.sock.setblocking(1)
Packit 6bd9ab
        self.fp = os.fdopen(self.sock.fileno(), 'r+b', 0)
Packit 6bd9ab
        # write a request header with a request code
Packit 6bd9ab
        self.action = action
Packit 6bd9ab
        self.write_int32(constants.NSLCD_VERSION)
Packit 6bd9ab
        self.write_int32(action)
Packit 6bd9ab
Packit 6bd9ab
    def write(self, value):
Packit 6bd9ab
        self.fp.write(value)
Packit 6bd9ab
Packit 6bd9ab
    def write_int32(self, value):
Packit 6bd9ab
        self.write(_int32.pack(value))
Packit 6bd9ab
Packit 6bd9ab
    def write_bytes(self, value):
Packit 6bd9ab
        self.write_int32(len(value))
Packit 6bd9ab
        self.write(value)
Packit 6bd9ab
Packit 6bd9ab
    def write_string(self, value):
Packit 6bd9ab
        if sys.version_info[0] >= 3:
Packit 6bd9ab
            value = value.encode('utf-8')
Packit 6bd9ab
        self.write_bytes(value.encode('utf-8'))
Packit 6bd9ab
Packit 6bd9ab
    def write_ether(self, value):
Packit 6bd9ab
        value = struct.pack('BBBBBB', *(int(x, 16) for x in value.split(':')))
Packit 6bd9ab
        self.write(value)
Packit 6bd9ab
Packit 6bd9ab
    def write_address(self, af, value):
Packit 6bd9ab
        self.write_int32(af)
Packit 6bd9ab
        self.write_bytes(value)
Packit 6bd9ab
Packit 6bd9ab
    def read(self, size):
Packit 6bd9ab
        value = b''
Packit 6bd9ab
        while len(value) < size:
Packit 6bd9ab
            data = self.fp.read(size - len(value))
Packit 6bd9ab
            if not data:
Packit 6bd9ab
                raise IOError('NSLCD protocol cut short')
Packit 6bd9ab
            value += data
Packit 6bd9ab
        return value
Packit 6bd9ab
Packit 6bd9ab
    def read_int32(self):
Packit 6bd9ab
        return _int32.unpack(self.read(_int32.size))[0]
Packit 6bd9ab
Packit 6bd9ab
    def read_bytes(self):
Packit 6bd9ab
        return self.read(self.read_int32())
Packit 6bd9ab
Packit 6bd9ab
    def read_string(self):
Packit 6bd9ab
        value = self.read_bytes()
Packit 6bd9ab
        if sys.version_info[0] >= 3:
Packit 6bd9ab
            value = value.decode('utf-8')
Packit 6bd9ab
        return value
Packit 6bd9ab
Packit 6bd9ab
    def read_stringlist(self):
Packit 6bd9ab
        num = self.read_int32()
Packit 6bd9ab
        return [self.read_string() for x in range(num)]
Packit 6bd9ab
Packit 6bd9ab
    def read_ether(self):
Packit 6bd9ab
        value = self.fp.read(6)
Packit 6bd9ab
        return ':'.join('%x' % x for x in struct.unpack('6B', value))
Packit 6bd9ab
Packit 6bd9ab
    def read_address(self):
Packit 6bd9ab
        af = self.read_int32()
Packit 6bd9ab
        return af, socket.inet_ntop(af, self.read_bytes())
Packit 6bd9ab
Packit 6bd9ab
    def read_addresslist(self):
Packit 6bd9ab
        num = self.read_int32()
Packit 6bd9ab
        return [self.read_address() for x in range(num)]
Packit 6bd9ab
Packit 6bd9ab
    def get_response(self):
Packit 6bd9ab
        # complete the request if required and check response header
Packit 6bd9ab
        if self.action:
Packit 6bd9ab
            # flush the stream
Packit 6bd9ab
            self.fp.flush()
Packit 6bd9ab
            # read and check response version number
Packit 6bd9ab
            if self.read_int32() != constants.NSLCD_VERSION:
Packit 6bd9ab
                raise IOError('NSLCD protocol error')
Packit 6bd9ab
            if self.read_int32() != self.action:
Packit 6bd9ab
                raise IOError('NSLCD protocol error')
Packit 6bd9ab
            # reset action to ensure that it is only the first time
Packit 6bd9ab
            self.action = None
Packit 6bd9ab
        # get the NSLCD_RESULT_* marker and return it
Packit 6bd9ab
        return self.read_int32()
Packit 6bd9ab
Packit 6bd9ab
    def close(self):
Packit 6bd9ab
        if hasattr(self, 'fp'):
Packit 6bd9ab
            try:
Packit 6bd9ab
                self.fp.close()
Packit 6bd9ab
            except IOError:
Packit 6bd9ab
                pass
Packit 6bd9ab
Packit 6bd9ab
    def __del__(self):
Packit 6bd9ab
        self.close()
Packit 6bd9ab
Packit 6bd9ab
Packit 6bd9ab
def usermod(username, asroot=False, password=None, args=None):
Packit 6bd9ab
    # open a connection to nslcd
Packit 6bd9ab
    con = NslcdClient(constants.NSLCD_ACTION_USERMOD)
Packit 6bd9ab
    # write the request information
Packit 6bd9ab
    con.write_string(username)
Packit 6bd9ab
    con.write_int32(1 if asroot else 0)
Packit 6bd9ab
    con.write_string(password)
Packit 6bd9ab
    for k, v in args.items():
Packit 6bd9ab
        con.write_int32(k)
Packit 6bd9ab
        con.write_string(v)
Packit 6bd9ab
    con.write_int32(constants.NSLCD_USERMOD_END)
Packit 6bd9ab
    # read the response
Packit 6bd9ab
    if con.get_response() != constants.NSLCD_RESULT_BEGIN:
Packit 6bd9ab
        raise IOError('NSLCD protocol error')
Packit 6bd9ab
    response = {}
Packit 6bd9ab
    while True:
Packit 6bd9ab
        key = con.read_int32()
Packit 6bd9ab
        if key == constants.NSLCD_USERMOD_END:
Packit 6bd9ab
            break
Packit 6bd9ab
        response[key] = con.read_string()
Packit 6bd9ab
    # return the response
Packit 6bd9ab
    return response