Esempio n. 1
0
    def handle(self, data, address, socket):
        self.sock = socket
        if not address in self.sessions.keys():
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            session = FakeSession(address[0], "", "", address[1])
            session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            #if not hasattr(self, 'session') or not self.session:
            #    return

            session.socket = self.sock
            self.sessions[address] = session
            self.initiate_session(data, address, session)
        else:
            session = self.sessions[address]
            # session already exists
            logger.debug('Incoming IPMI traffic from %s', address)
            if session.stage == 0:
                self.close_server_session(session)
            else:
                self._got_request(data, address, session)
Esempio n. 2
0
 def test(ivr_class, callerid=None, interactive = False, sound = False, **kwargs):
     session = FakeSession(interactive = interactive, sound = sound)
     if callerid:
         session.callerid = callerid
     testivr = ivr_class("fakeDnis", session, None)
     for k in kwargs:
         setattr(testivr, k, kwargs[k])
     testivr.debug = True
     testivr.handler()
     return testivr.debug
Esempio n. 3
0
    def handle(self, data, address, socket):
        self.sock = socket
        # make sure self.session exists
        if not (address[0] in self.sessions.keys() and self.sessions[address[0]].port == address[1]) or not hasattr(self, 'session'):
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            self.session = FakeSession(address[0], "", "", address[1])
            self.session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            if not hasattr(self, 'session') or not self.session:
                return

            self.session.socket = self.sock
            self.sessions[address[0]] = self.session
            self.initiate_session(data, address, self.session)
        else:
            # session already exists
            logger.debug('Incoming IPMI traffic from %s', address)
            if self.session.stage == 0:
                self.close_server_session()
            else:
                self._got_request(data, address, self.session)
Esempio n. 4
0
    def handle(self, data, address):
        if not address[0] in self.sessions.keys():
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            self.session = FakeSession(address[0], "", "", address[1])
            self.session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            self.session.socket = self.sock
            self.sessions[address[0]] = self.session
            self.initiate_session(data, address, self.session)
        else:
            # session already exists
            logger.info('Incoming IPMI traffic from %s', address)
            if self.session.stage == 0:
                self.close_server_session()
            else:
                self._got_request(data, address, self.session)
Esempio n. 5
0
    def handle(self, data, address):
        if not address[0] in self.sessions.keys():
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            self.session = FakeSession(address[0], "", "", address[1])
            self.session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            self.session.socket = self.sock
            self.sessions[address[0]] = self.session
            self.initiate_session(data, address, self.session)
        else:
            # session already exists
            logger.info('Incoming IPMI traffic from %s', address)
            if self.session.stage == 0:
                self.close_server_session()
            else:
                self._got_request(data, address, self.session)
Esempio n. 6
0
class IpmiServerContext(object):

    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance == None or (len(args) > 0 and args[0] == 'reset'):
            cls.__instance = object.__new__(cls)
            cls.__instance.name = "IpmiServer Context"

            # Initialize ctx state
            self = cls.__instance
            self.device_name = "CloudStack IPMI Sim"
            self.sessions = dict()
            self.uuid = uuid.uuid4()
            self.kg = None
            self.authdata = collections.OrderedDict()

            lanchannel = 1
            authtype = 0b10000000
            authstatus = 0b00000100
            chancap = 0b00000010
            oemdata = (0, 0, 0, 0)
            self.authcap = struct.pack('BBBBBBBBB', 0, lanchannel, authtype, authstatus, chancap, *oemdata)
            self.bmc = self._configure_users()
            logger.info('CloudStack IPMI Sim BMC initialized')
        return cls.__instance

    def _configure_users(self):
        # XML parsing
        authdata_name = ["admin", "operator", "user"]
        authdata_passwd = ["password", "oppassword", "userpassword"]
        self.authdata = collections.OrderedDict(zip(authdata_name, authdata_passwd))

        authdata_priv = [4, 3, 2]
        if False in map(lambda k: 0 < int(k) <= 4, authdata_priv):
            raise ValueError("Privilege level must be between 1 and 4")
        authdata_priv = [int(k) for k in authdata_priv]
        self.privdata = collections.OrderedDict(zip(authdata_name, authdata_priv))

        activeusers = ['true', 'false', 'true']
        self.activeusers = [1, 0, 1]
        self.fixedusers = [1, 1, 1]

        self.channelaccessdata = collections.OrderedDict(zip(authdata_name, activeusers))

        return FakeBmc(self.authdata)

    def _checksum(self, *data):
        csum = sum(data)
        csum ^= 0xff
        csum += 1
        csum &= 0xff
        return csum

    def handle(self, data, address, socket):
        self.sock = socket
        # make sure self.session exists
        if not (address[0] in self.sessions.keys() and self.sessions[address[0]].port == address[1]) or not hasattr(self, 'session'):
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            self.session = FakeSession(address[0], "", "", address[1])
            self.session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            if not hasattr(self, 'session') or not self.session:
                return

            self.session.socket = self.sock
            self.sessions[address[0]] = self.session
            self.initiate_session(data, address, self.session)
        else:
            # session already exists
            logger.debug('Incoming IPMI traffic from %s', address)
            if self.session.stage == 0:
                self.close_server_session()
            else:
                self._got_request(data, address, self.session)

    def initiate_session(self, data, address, session):
        if len(data) < 22:
            self.close_server_session()
            return
        if not (data[0] == '\x06' and data[2:4] == '\xff\x07'):
            # check rmcp version, sequencenumber and class;
            self.close_server_session()
            return
        if data[4] == '\x06':
            # ipmi v2
            session.ipmiversion = 2.0
            session.authtype = 6
            payload_type = data[5]
            if payload_type not in ('\x00', '\x10'):
                self.close_server_session()
                return
            if payload_type == '\x10':
                # new session to handle conversation
                serversession.ServerSession(self.authdata, self.kg, session.sockaddr,
                                            self.sock, data[16:], self.uuid, bmc=self)
                return
            data = data[13:]
        myaddr, netfnlun = struct.unpack('2B', data[14:16])
        netfn = (netfnlun & 0b11111100) >> 2
        mylun = netfnlun & 0b11
        if netfn == 6:
            # application request
            if data[19] == '\x38':
                # cmd = get channel auth capabilities
                verchannel, level = struct.unpack('2B', data[20:22])
                version = verchannel & 0b10000000
                if version != 0b10000000:
                    self.close_server_session()
                    return
                channel = verchannel & 0b1111
                if channel != 0xe:
                    self.close_server_session()
                    return
                (clientaddr, clientlun) = struct.unpack('BB', data[17:19])
                level &= 0b1111
                self.send_auth_cap(myaddr, mylun, clientaddr, clientlun, session.sockaddr)

    def send_auth_cap(self, myaddr, mylun, clientaddr, clientlun, sockaddr):
        header = '\x06\x00\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10'

        headerdata = (clientaddr, clientlun | (7 << 2))
        headersum = self._checksum(*headerdata)
        header += struct.pack('BBBBBB', *(headerdata + (headersum, myaddr, mylun, 0x38)))
        header += self.authcap
        bodydata = struct.unpack('B' * len(header[17:]), header[17:])
        header += chr(self._checksum(*bodydata))
        self.session.stage += 1
        logger.debug('Connection established with %s', sockaddr)
        self.session.send_data(header, sockaddr)

    def close_server_session(self):
        logger.debug('IPMI Session closed %s', self.session.sockaddr[0])
        # cleanup session
        del self.sessions[self.session.sockaddr[0]]
        del self.session

    def _got_request(self, data, address, session):
        if data[4] in ('\x00', '\x02'):
            # ipmi 1.5 payload
            session.ipmiversion = 1.5
            remsequencenumber = struct.unpack('<I', data[5:9])[0]
            if hasattr(session, 'remsequencenumber') and remsequencenumber < session.remsequencenumber:
                self.close_server_session()
                return
            session.remsequencenumber = remsequencenumber
            if ord(data[4]) != session.authtype:
                self.close_server_session()
                return
            remsessid = struct.unpack("<I", data[9:13])[0]
            if remsessid != session.sessionid:
                self.close_server_session()
                return
            rsp = list(struct.unpack("!%dB" % len(data), data))
            authcode = False
            if data[4] == '\x02':
                # authcode in ipmi 1.5 packet
                authcode = data[13:29]
                del rsp[13:29]
            payload = list(rsp[14:14 + rsp[13]])
            if authcode:
                expectedauthcode = session._ipmi15authcode(payload, checkremotecode=True)
                expectedauthcode = struct.pack("%dB" % len(expectedauthcode), *expectedauthcode)
                if expectedauthcode != authcode:
                    self.close_server_session()
                    return
            session._ipmi15(payload)
        elif data[4] == '\x06':
            # ipmi 2.0 payload
            session.ipmiversion = 2.0
            session.authtype = 6
            session._ipmi20(data)
        else:
            # unrecognized data
            self.close_server_session()
            return

    def _got_rmcp_openrequest(self, data):
        request = struct.pack('B' * len(data), *data)
        clienttag = ord(request[0])
        self.clientsessionid = list(struct.unpack('4B', request[4:8]))
        self.managedsessionid = list(struct.unpack('4B', os.urandom(4)))
        self.session.privlevel = 4
        response = ([clienttag, 0, self.session.privlevel, 0] +
                    self.clientsessionid + self.managedsessionid +
                    [
                        0, 0, 0, 8, 1, 0, 0, 0,  # auth
                        1, 0, 0, 8, 1, 0, 0, 0,  # integrity
                        2, 0, 0, 8, 1, 0, 0, 0,  # privacy
        ])
        logger.debug('IPMI open session request')
        self.session.send_payload(response, constants.payload_types['rmcpplusopenresponse'], retry=False)

    def _got_rakp1(self, data):
        clienttag = data[0]
        self.Rm = data[8:24]
        self.rolem = data[24]
        self.maxpriv = self.rolem & 0b111
        namepresent = data[27]
        if namepresent == 0:
            self.close_server_session()
            return
        usernamebytes = data[28:]
        self.username = struct.pack('%dB' % len(usernamebytes), *usernamebytes)
        if self.username not in self.authdata:
            self.close_server_session()
            return
        uuidbytes = self.uuid.bytes
        uuidbytes = list(struct.unpack('%dB' % len(uuidbytes), uuidbytes))
        self.uuiddata = uuidbytes
        self.Rc = list(struct.unpack('16B', os.urandom(16)))
        hmacdata = (self.clientsessionid + self.managedsessionid + self.Rm + self.Rc + uuidbytes +
                    [self.rolem, len(self.username)])
        hmacdata = struct.pack('%dB' % len(hmacdata), *hmacdata)
        hmacdata += self.username
        self.kuid = self.authdata[self.username]
        if self.kg is None:
            self.kg = self.kuid
        authcode = hmac.new(self.kuid, hmacdata, hashlib.sha1).digest()
        authcode = list(struct.unpack('%dB' % len(authcode), authcode))
        newmessage = ([clienttag, 0, 0, 0] + self.clientsessionid + self.Rc + uuidbytes + authcode)
        logger.debug('IPMI rakp1 request')
        self.session.send_payload(newmessage, constants.payload_types['rakp2'], retry=False)

    def _got_rakp3(self, data):
        RmRc = struct.pack('B' * len(self.Rm + self.Rc), *(self.Rm + self.Rc))
        self.sik = hmac.new(self.kg, RmRc + struct.pack("2B", self.rolem, len(self.username)) +
                            self.username, hashlib.sha1).digest()
        self.session.k1 = hmac.new(self.sik, '\x01' * 20, hashlib.sha1).digest()
        self.session.k2 = hmac.new(self.sik, '\x02' * 20, hashlib.sha1).digest()
        self.session.aeskey = self.session.k2[0:16]

        hmacdata = struct.pack('B' * len(self.Rc), *self.Rc) + struct.pack("4B", *self.clientsessionid) +\
            struct.pack("2B", self.rolem, len(self.username)) + self.username
        expectedauthcode = hmac.new(self.kuid, hmacdata, hashlib.sha1).digest()
        authcode = struct.pack("%dB" % len(data[8:]), *data[8:])
        if expectedauthcode != authcode:
            self.close_server_session()
            return
        clienttag = data[0]
        if data[1] != 0:
            self.close_server_session()
            return
        self.session.localsid = struct.unpack('<I', struct.pack('4B', *self.managedsessionid))[0]

        logger.debug('IPMI rakp3 request')
        self.session.ipmicallback = self.handle_client_request
        self._send_rakp4(clienttag, 0)

    def _send_rakp4(self, tagvalue, statuscode):
        payload = [tagvalue, statuscode, 0, 0] + self.clientsessionid
        hmacdata = self.Rm + self.managedsessionid + self.uuiddata
        hmacdata = struct.pack('%dB' % len(hmacdata), *hmacdata)
        authdata = hmac.new(self.sik, hmacdata, hashlib.sha1).digest()[:12]
        payload += struct.unpack('%dB' % len(authdata), authdata)
        logger.debug('IPMI rakp4 sent')
        self.session.send_payload(payload, constants.payload_types['rakp4'], retry=False)
        self.session.confalgo = 'aes'
        self.session.integrityalgo = 'sha1'
        self.session.sessionid = struct.unpack('<I', struct.pack('4B', *self.clientsessionid))[0]

    def handle_client_request(self, request):
        if request['netfn'] == 6 and request['command'] == 0x3b:
            # set session privilage level
            pendingpriv = request['data'][0]
            returncode = 0
            if pendingpriv > 1:
                if pendingpriv > self.maxpriv:
                    returncode = 0x81
                else:
                    self.clientpriv = request['data'][0]
            self.session._send_ipmi_net_payload(code=returncode, data=[self.clientpriv])
            logger.debug('IPMI response sent (Set Session Privilege) to %s', self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x3c:
            # close session
            self.session.send_ipmi_response()
            logger.debug('IPMI response sent (Close Session) to %s', self.session.sockaddr)
            self.close_server_session()
        elif request['netfn'] == 6 and request['command'] == 0x44:
            # get user access
            reschan = request['data'][0]
            channel = reschan & 0b00001111
            resuid = request['data'][1]
            usid = resuid & 0b00011111
            if self.clientpriv > self.maxpriv:
                returncode = 0xd4
            else:
                returncode = 0
            self.usercount = len(self.authdata.keys())
            self.channelaccess = 0b0000000 | self.privdata[self.authdata.keys()[usid - 1]]
            if self.channelaccessdata[self.authdata.keys()[usid - 1]] == 'true':
                # channelaccess: 7=res; 6=callin; 5=link; 4=messaging; 3-0=privilege
                self.channelaccess |= 0b00110000

            data = list()
            data.append(self.usercount)
            data.append(sum(self.activeusers))
            data.append(sum(self.fixedusers))
            data.append(self.channelaccess)
            self.session._send_ipmi_net_payload(code=returncode, data=data)
            logger.debug('IPMI response sent (Get User Access) to %s', self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x46:
            # get user name
            userid = request['data'][0]
            returncode = 0
            username = self.authdata.keys()[userid - 1]
            data = map(ord, list(username))
            while len(data) < 16:
                # filler
                data.append(0)
            self.session._send_ipmi_net_payload(code=returncode, data=data)
            logger.debug('IPMI response sent (Get User Name) to %s', self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x45:
            # set user name
            # TODO: fix issue where users can be overwritten
            # python does not support dictionary with duplicate keys
            userid = request['data'][0]
            username = ''.join(chr(x) for x in request['data'][1:]).strip('\x00')
            oldname = self.authdata.keys()[userid - 1]
            # need to recreate dictionary to preserve order
            self.copyauth = collections.OrderedDict()
            self.copypriv = collections.OrderedDict()
            self.copychannel = collections.OrderedDict()
            index = 0
            for k, v in self.authdata.iteritems():
                if index == userid - 1:
                    self.copyauth.update({username: self.authdata[oldname]})
                    self.copypriv.update({username: self.privdata[oldname]})
                    self.copychannel.update({username: self.channelaccessdata[oldname]})
                else:
                    self.copyauth.update({k: v})
                    self.copypriv.update({k: self.privdata[k]})
                    self.copychannel.update({k: self.channelaccessdata[k]})
                index += 1
            self.authdata = self.copyauth
            self.privdata = self.copypriv
            self.channelaccessdata = self.copychannel

            returncode = 0
            self.session._send_ipmi_net_payload(code=returncode)
            logger.debug('IPMI response sent (Set User Name) to %s', self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x47:
            # set user passwd
            passwd_length = request['data'][0] & 0b10000000
            userid = request['data'][0] & 0b00111111
            username = self.authdata.keys()[userid - 1]
            operation = request['data'][1] & 0b00000011
            returncode = 0

            if passwd_length:
                # 20 byte
                passwd = ''.join(chr(x) for x in request['data'][2:22])
            else:
                # 16 byte
                passwd = ''.join(chr(x) for x in request['data'][2:18])
            if operation == 0:
                # disable user
                if self.activeusers[self.authdata.keys().index(username)]:
                    self.activeusers[self.authdata.keys().index(username)] = 0
            elif operation == 1:
                # enable user
                if not self.activeusers[self.authdata.keys().index(username)]:
                    self.activeusers[self.authdata.keys().index(username)] = 1
            elif operation == 2:
                # set passwd
                if len(passwd) not in [16, 20]:
                    returncode = 0x81
                self.authdata[username] = passwd.strip('\x00')
            else:
                # test passwd
                if len(passwd) not in [16, 20]:
                    returncode = 0x81
                if self.authdata[username] != passwd.strip('\x00'):
                    returncode = 0x80

            self.session._send_ipmi_net_payload(code=returncode)
            logger.info('IPMI response sent (Set User Password) to %s', self.session.sockaddr)
        elif request['netfn'] in [0, 6] and request['command'] in [1, 2, 8, 9]:
            self.bmc.handle_raw_request(request, self.session)
        else:
            returncode = 0xc1
            self.session._send_ipmi_net_payload(code=returncode)
            logger.debug('IPMI unrecognized command from %s', self.session.sockaddr)
            logger.debug('IPMI response sent (Invalid Command) to %s', self.session.sockaddr)
Esempio n. 7
0
class IpmiServerContext(object):

    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance == None or (len(args) > 0 and args[0] == 'reset'):
            cls.__instance = object.__new__(cls)
            cls.__instance.name = "IpmiServer Context"

            # Initialize ctx state
            self = cls.__instance
            self.device_name = "CloudStack IPMI Sim"
            self.sessions = dict()
            self.uuid = uuid.uuid4()
            self.kg = None
            self.authdata = collections.OrderedDict()

            lanchannel = 1
            authtype = 0b10000000
            authstatus = 0b00000100
            chancap = 0b00000010
            oemdata = (0, 0, 0, 0)
            self.authcap = struct.pack('BBBBBBBBB', 0, lanchannel, authtype,
                                       authstatus, chancap, *oemdata)
            self.bmc = self._configure_users()
            logger.info('CloudStack IPMI Sim BMC initialized')
        return cls.__instance

    def _configure_users(self):
        # XML parsing
        authdata_name = ["admin", "operator", "user"]
        authdata_passwd = ["password", "oppassword", "userpassword"]
        self.authdata = collections.OrderedDict(
            zip(authdata_name, authdata_passwd))

        authdata_priv = [4, 3, 2]
        if False in map(lambda k: 0 < int(k) <= 4, authdata_priv):
            raise ValueError("Privilege level must be between 1 and 4")
        authdata_priv = [int(k) for k in authdata_priv]
        self.privdata = collections.OrderedDict(
            zip(authdata_name, authdata_priv))

        activeusers = ['true', 'false', 'true']
        self.activeusers = [1, 0, 1]
        self.fixedusers = [1, 1, 1]

        self.channelaccessdata = collections.OrderedDict(
            zip(authdata_name, activeusers))

        return FakeBmc(self.authdata)

    def _checksum(self, *data):
        csum = sum(data)
        csum ^= 0xff
        csum += 1
        csum &= 0xff
        return csum

    def handle(self, data, address, socket):
        self.sock = socket
        # make sure self.session exists
        if not (address[0] in self.sessions.keys() and
                self.sessions[address[0]].port == address[1]) or not hasattr(
                    self, 'session'):
            # new session for new source
            logger.info('New IPMI traffic from %s', address)
            self.session = FakeSession(address[0], "", "", address[1])
            self.session.server = self

            self.uuid = uuid.uuid4()
            self.kg = None

            if not hasattr(self, 'session') or not self.session:
                return

            self.session.socket = self.sock
            self.sessions[address[0]] = self.session
            self.initiate_session(data, address, self.session)
        else:
            # session already exists
            logger.debug('Incoming IPMI traffic from %s', address)
            if self.session.stage == 0:
                self.close_server_session()
            else:
                self._got_request(data, address, self.session)

    def initiate_session(self, data, address, session):
        if len(data) < 22:
            self.close_server_session()
            return
        if not (data[0] == '\x06' and data[2:4] == '\xff\x07'):
            # check rmcp version, sequencenumber and class;
            self.close_server_session()
            return
        if data[4] == '\x06':
            # ipmi v2
            session.ipmiversion = 2.0
            session.authtype = 6
            payload_type = data[5]
            if payload_type not in ('\x00', '\x10'):
                self.close_server_session()
                return
            if payload_type == '\x10':
                # new session to handle conversation
                serversession.ServerSession(self.authdata,
                                            self.kg,
                                            session.sockaddr,
                                            self.sock,
                                            data[16:],
                                            self.uuid,
                                            bmc=self)
                return
            data = data[13:]
        myaddr, netfnlun = struct.unpack('2B', data[14:16])
        netfn = (netfnlun & 0b11111100) >> 2
        mylun = netfnlun & 0b11
        if netfn == 6:
            # application request
            if data[19] == '\x38':
                # cmd = get channel auth capabilities
                verchannel, level = struct.unpack('2B', data[20:22])
                version = verchannel & 0b10000000
                if version != 0b10000000:
                    self.close_server_session()
                    return
                channel = verchannel & 0b1111
                if channel != 0xe:
                    self.close_server_session()
                    return
                (clientaddr, clientlun) = struct.unpack('BB', data[17:19])
                level &= 0b1111
                self.send_auth_cap(myaddr, mylun, clientaddr, clientlun,
                                   session.sockaddr)

    def send_auth_cap(self, myaddr, mylun, clientaddr, clientlun, sockaddr):
        header = '\x06\x00\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10'

        headerdata = (clientaddr, clientlun | (7 << 2))
        headersum = self._checksum(*headerdata)
        header += struct.pack('BBBBBB',
                              *(headerdata + (headersum, myaddr, mylun, 0x38)))
        header += self.authcap
        bodydata = struct.unpack('B' * len(header[17:]), header[17:])
        header += chr(self._checksum(*bodydata))
        self.session.stage += 1
        logger.debug('Connection established with %s', sockaddr)
        self.session.send_data(header, sockaddr)

    def close_server_session(self):
        logger.debug('IPMI Session closed %s', self.session.sockaddr[0])
        # cleanup session
        del self.sessions[self.session.sockaddr[0]]
        del self.session

    def _got_request(self, data, address, session):
        if data[4] in ('\x00', '\x02'):
            # ipmi 1.5 payload
            session.ipmiversion = 1.5
            remsequencenumber = struct.unpack('<I', data[5:9])[0]
            if hasattr(session, 'remsequencenumber'
                       ) and remsequencenumber < session.remsequencenumber:
                self.close_server_session()
                return
            session.remsequencenumber = remsequencenumber
            if ord(data[4]) != session.authtype:
                self.close_server_session()
                return
            remsessid = struct.unpack("<I", data[9:13])[0]
            if remsessid != session.sessionid:
                self.close_server_session()
                return
            rsp = list(struct.unpack("!%dB" % len(data), data))
            authcode = False
            if data[4] == '\x02':
                # authcode in ipmi 1.5 packet
                authcode = data[13:29]
                del rsp[13:29]
            payload = list(rsp[14:14 + rsp[13]])
            if authcode:
                expectedauthcode = session._ipmi15authcode(
                    payload, checkremotecode=True)
                expectedauthcode = struct.pack("%dB" % len(expectedauthcode),
                                               *expectedauthcode)
                if expectedauthcode != authcode:
                    self.close_server_session()
                    return
            session._ipmi15(payload)
        elif data[4] == '\x06':
            # ipmi 2.0 payload
            session.ipmiversion = 2.0
            session.authtype = 6
            session._ipmi20(data)
        else:
            # unrecognized data
            self.close_server_session()
            return

    def _got_rmcp_openrequest(self, data):
        request = struct.pack('B' * len(data), *data)
        clienttag = ord(request[0])
        self.clientsessionid = list(struct.unpack('4B', request[4:8]))
        self.managedsessionid = list(struct.unpack('4B', os.urandom(4)))
        self.session.privlevel = 4
        response = ([clienttag, 0, self.session.privlevel, 0] +
                    self.clientsessionid + self.managedsessionid + [
                        0,
                        0,
                        0,
                        8,
                        1,
                        0,
                        0,
                        0,  # auth
                        1,
                        0,
                        0,
                        8,
                        1,
                        0,
                        0,
                        0,  # integrity
                        2,
                        0,
                        0,
                        8,
                        1,
                        0,
                        0,
                        0,  # privacy
                    ])
        logger.debug('IPMI open session request')
        self.session.send_payload(
            response,
            constants.payload_types['rmcpplusopenresponse'],
            retry=False)

    def _got_rakp1(self, data):
        clienttag = data[0]
        self.Rm = data[8:24]
        self.rolem = data[24]
        self.maxpriv = self.rolem & 0b111
        namepresent = data[27]
        if namepresent == 0:
            self.close_server_session()
            return
        usernamebytes = data[28:]
        self.username = struct.pack('%dB' % len(usernamebytes), *usernamebytes)
        if self.username not in self.authdata:
            self.close_server_session()
            return
        uuidbytes = self.uuid.bytes
        uuidbytes = list(struct.unpack('%dB' % len(uuidbytes), uuidbytes))
        self.uuiddata = uuidbytes
        self.Rc = list(struct.unpack('16B', os.urandom(16)))
        hmacdata = (self.clientsessionid + self.managedsessionid + self.Rm +
                    self.Rc + uuidbytes +
                    [self.rolem, len(self.username)])
        hmacdata = struct.pack('%dB' % len(hmacdata), *hmacdata)
        hmacdata += self.username
        self.kuid = self.authdata[self.username]
        if self.kg is None:
            self.kg = self.kuid
        authcode = hmac.new(self.kuid, hmacdata, hashlib.sha1).digest()
        authcode = list(struct.unpack('%dB' % len(authcode), authcode))
        newmessage = ([clienttag, 0, 0, 0] + self.clientsessionid + self.Rc +
                      uuidbytes + authcode)
        logger.debug('IPMI rakp1 request')
        self.session.send_payload(newmessage,
                                  constants.payload_types['rakp2'],
                                  retry=False)

    def _got_rakp3(self, data):
        RmRc = struct.pack('B' * len(self.Rm + self.Rc), *(self.Rm + self.Rc))
        self.sik = hmac.new(
            self.kg, RmRc + struct.pack("2B", self.rolem, len(self.username)) +
            self.username, hashlib.sha1).digest()
        self.session.k1 = hmac.new(self.sik, '\x01' * 20,
                                   hashlib.sha1).digest()
        self.session.k2 = hmac.new(self.sik, '\x02' * 20,
                                   hashlib.sha1).digest()
        self.session.aeskey = self.session.k2[0:16]

        hmacdata = struct.pack('B' * len(self.Rc), *self.Rc) + struct.pack("4B", *self.clientsessionid) +\
            struct.pack("2B", self.rolem, len(self.username)) + self.username
        expectedauthcode = hmac.new(self.kuid, hmacdata, hashlib.sha1).digest()
        authcode = struct.pack("%dB" % len(data[8:]), *data[8:])
        if expectedauthcode != authcode:
            self.close_server_session()
            return
        clienttag = data[0]
        if data[1] != 0:
            self.close_server_session()
            return
        self.session.localsid = struct.unpack(
            '<I', struct.pack('4B', *self.managedsessionid))[0]

        logger.debug('IPMI rakp3 request')
        self.session.ipmicallback = self.handle_client_request
        self._send_rakp4(clienttag, 0)

    def _send_rakp4(self, tagvalue, statuscode):
        payload = [tagvalue, statuscode, 0, 0] + self.clientsessionid
        hmacdata = self.Rm + self.managedsessionid + self.uuiddata
        hmacdata = struct.pack('%dB' % len(hmacdata), *hmacdata)
        authdata = hmac.new(self.sik, hmacdata, hashlib.sha1).digest()[:12]
        payload += struct.unpack('%dB' % len(authdata), authdata)
        logger.debug('IPMI rakp4 sent')
        self.session.send_payload(payload,
                                  constants.payload_types['rakp4'],
                                  retry=False)
        self.session.confalgo = 'aes'
        self.session.integrityalgo = 'sha1'
        self.session.sessionid = struct.unpack(
            '<I', struct.pack('4B', *self.clientsessionid))[0]

    def handle_client_request(self, request):
        if request['netfn'] == 6 and request['command'] == 0x3b:
            # set session privilage level
            pendingpriv = request['data'][0]
            returncode = 0
            if pendingpriv > 1:
                if pendingpriv > self.maxpriv:
                    returncode = 0x81
                else:
                    self.clientpriv = request['data'][0]
            self.session._send_ipmi_net_payload(code=returncode,
                                                data=[self.clientpriv])
            logger.debug('IPMI response sent (Set Session Privilege) to %s',
                         self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x3c:
            # close session
            self.session.send_ipmi_response()
            logger.debug('IPMI response sent (Close Session) to %s',
                         self.session.sockaddr)
            self.close_server_session()
        elif request['netfn'] == 6 and request['command'] == 0x44:
            # get user access
            reschan = request['data'][0]
            channel = reschan & 0b00001111
            resuid = request['data'][1]
            usid = resuid & 0b00011111
            if self.clientpriv > self.maxpriv:
                returncode = 0xd4
            else:
                returncode = 0
            self.usercount = len(self.authdata.keys())
            self.channelaccess = 0b0000000 | self.privdata[
                self.authdata.keys()[usid - 1]]
            if self.channelaccessdata[self.authdata.keys()[usid -
                                                           1]] == 'true':
                # channelaccess: 7=res; 6=callin; 5=link; 4=messaging; 3-0=privilege
                self.channelaccess |= 0b00110000

            data = list()
            data.append(self.usercount)
            data.append(sum(self.activeusers))
            data.append(sum(self.fixedusers))
            data.append(self.channelaccess)
            self.session._send_ipmi_net_payload(code=returncode, data=data)
            logger.debug('IPMI response sent (Get User Access) to %s',
                         self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x46:
            # get user name
            userid = request['data'][0]
            returncode = 0
            username = self.authdata.keys()[userid - 1]
            data = map(ord, list(username))
            while len(data) < 16:
                # filler
                data.append(0)
            self.session._send_ipmi_net_payload(code=returncode, data=data)
            logger.debug('IPMI response sent (Get User Name) to %s',
                         self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x45:
            # set user name
            # TODO: fix issue where users can be overwritten
            # python does not support dictionary with duplicate keys
            userid = request['data'][0]
            username = ''.join(chr(x)
                               for x in request['data'][1:]).strip('\x00')
            oldname = self.authdata.keys()[userid - 1]
            # need to recreate dictionary to preserve order
            self.copyauth = collections.OrderedDict()
            self.copypriv = collections.OrderedDict()
            self.copychannel = collections.OrderedDict()
            index = 0
            for k, v in self.authdata.iteritems():
                if index == userid - 1:
                    self.copyauth.update({username: self.authdata[oldname]})
                    self.copypriv.update({username: self.privdata[oldname]})
                    self.copychannel.update(
                        {username: self.channelaccessdata[oldname]})
                else:
                    self.copyauth.update({k: v})
                    self.copypriv.update({k: self.privdata[k]})
                    self.copychannel.update({k: self.channelaccessdata[k]})
                index += 1
            self.authdata = self.copyauth
            self.privdata = self.copypriv
            self.channelaccessdata = self.copychannel

            returncode = 0
            self.session._send_ipmi_net_payload(code=returncode)
            logger.debug('IPMI response sent (Set User Name) to %s',
                         self.session.sockaddr)
        elif request['netfn'] == 6 and request['command'] == 0x47:
            # set user passwd
            passwd_length = request['data'][0] & 0b10000000
            userid = request['data'][0] & 0b00111111
            username = self.authdata.keys()[userid - 1]
            operation = request['data'][1] & 0b00000011
            returncode = 0

            if passwd_length:
                # 20 byte
                passwd = ''.join(chr(x) for x in request['data'][2:22])
            else:
                # 16 byte
                passwd = ''.join(chr(x) for x in request['data'][2:18])
            if operation == 0:
                # disable user
                if self.activeusers[self.authdata.keys().index(username)]:
                    self.activeusers[self.authdata.keys().index(username)] = 0
            elif operation == 1:
                # enable user
                if not self.activeusers[self.authdata.keys().index(username)]:
                    self.activeusers[self.authdata.keys().index(username)] = 1
            elif operation == 2:
                # set passwd
                if len(passwd) not in [16, 20]:
                    returncode = 0x81
                self.authdata[username] = passwd.strip('\x00')
            else:
                # test passwd
                if len(passwd) not in [16, 20]:
                    returncode = 0x81
                if self.authdata[username] != passwd.strip('\x00'):
                    returncode = 0x80

            self.session._send_ipmi_net_payload(code=returncode)
            logger.info('IPMI response sent (Set User Password) to %s',
                        self.session.sockaddr)
        elif request['netfn'] in [0, 6] and request['command'] in [1, 2, 8, 9]:
            self.bmc.handle_raw_request(request, self.session)
        else:
            returncode = 0xc1
            self.session._send_ipmi_net_payload(code=returncode)
            logger.debug('IPMI unrecognized command from %s',
                         self.session.sockaddr)
            logger.debug('IPMI response sent (Invalid Command) to %s',
                         self.session.sockaddr)