Solarachadh fèin-ghluasadach Yealink T19 + leabhar seòlaidhean fiùghantach

Nuair a thàinig mi a dh’ obair don chompanaidh seo, bha stòr-dàta de dh’ innealan IP agam mu thràth, grunn luchd-frithealaidh le rionnag agus bad ann an cruth FreeBPX. A bharrachd air an sin, bha an analogue PBX Samsung IDCS500 ag obair ann an co-shìnte agus, san fharsaingeachd, b 'e am prìomh shiostam conaltraidh anns a' chompanaidh; Bha telephony IP ag obair a-mhàin airson an roinn reic. Agus bhiodh a h-uile càil air leantainn air adhart a’ còcaireachd mar seo, ach aon latha math chaidh òrdugh a thoirt seachad airson a h-uile duine a ghluasad gu fòn IP, chaidh cinn-latha aontachadh, chaidh uidheamachd a cheannach agus thòisich am plana gus an iomairt a ghluasad chun 21mh linn a chuir an gnìomh.
Is e a 'chiad rud a tha a' tòiseachadh a 'gabhail dragh ann an suidheachadh mar seo an àireamh de sheataichean fòn a tha a' fàs gu luath a dh'fheumas a bhith air an riaghladh ann an dòigh air choreigin, agus b 'e an dàrna rud a bha gu math draghail an leabhar fòn. Nam b’ urrainn do Mhanaidsear Endpoint ar cuideachadh leis a’ chiad fhear (a chaidh, leis an t-slighe, a ghearradh a-mach às na dreachan as ùire de FreePBX), dh’ èirich cuid de cheistean mun leabhar:

  • An toiseach, ciamar a nì thu cinnteach gu bheil e ceart nuair a tha suidheachadh / fileantachd luchd-cleachdaidh an-còmhnaidh ag atharrachadh?
  • San dàrna h-àite, mar as urrainn dhut fònaichean a dhì-phearsanachadh gu tur. Agus nach lìon thu an t-ainm-conaltraidh a h-uile turas?

Bha an duilgheadas inntinneach, cha do ghabh am fuasgladh fada ri thighinn. A-nis bheir mi an liosta slàn, agus an uairsin seallaidh sinn air ann an òrdugh.

from scapy.all import sniff
from scapy.layers.inet import IP
import mysql.connector
import ldap
import getpass
import tftpy
import requests
import os
import time
from string import replace

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=IT,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname


def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)


def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account


def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr


def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()


def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password


if __name__ == '__main__':
    macaddr, ipaddr = sniff_frame()
    current_account = get_phone_inform(ipaddr)
    current_account_password = check_account(current_account)
    macaddr = macaddr.replace(':', '')
    ipaddr = ipaddr.decode('utf-8')
    adname = conn_ldap(getpass.getuser())
    query = 'INSERT INTO station (mac, ip, name, number) VALUES (' + '"' + macaddr + '",' + '"' + ipaddr + '",' + '"' + adname + '",' + '"' + get_phone_inform(ipaddr) + '"' + ')'
    qwery2 = 'UPDATE station SET ip=' + '"' + ipaddr + '"' + ', name=' + '"' + adname + '"' + ', number=' + '"' + get_phone_inform(ipaddr) + '"' + ' WHERE mac=' + '"' + macaddr + '"'
    fquery = 'SELECT EXISTS(SELECT mac FROM voip.station WHERE mac=' + '"' + macaddr + '")'
    query = query.encode('utf-8')
    fquery = fquery.encode('utf-8')
    config = macaddr + '.cfg'
    place = os.path.expanduser("~") + "" + "AppDataLocal" + config
    conn_mysql(query,fquery,macaddr,qwery2)
    tftp_file_change(config,place,adname,current_account,current_account_password)
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
    requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Bidh am prògram a’ ruith air coimpiutair an neach-cleachdaidh agus ag obair fhad ‘s a tha an coimpiutair ceangailte ris an lìonra tro fhòn, leis nach urrainn dha Yealink T19 obrachadh mar gheata.

An toiseach, feumaidh sinn tuigsinn a bheil e ceangailte? agus dè am mac agus ip a th’ aig ar fòn.

def sniff_frame():
    pcapf = sniff(count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060")
    if len(pcapf) == 0:
        exit()
    frame = pcapf[0]
    macaddr = frame.src
    print macaddr[:8]
    if macaddr[:8] != '80:5e:c0':
        exit()
    ipaddr = frame[0][IP].src
    return macaddr, ipaddr

An seo bidh sinn a’ cleachdadh a’ ghnìomh sniff bhon fhrèam sgapy, le a chuideachadh gheibh sinn pasgan udp ro-shuidhichte, fuirich 70 diogan agus mura glac sinn dad, fàgaidh sinn.

count=1, timeout=70, filter="dst host 192.168.0.3 and port 5060"

An ath rud, bidh sinn a 'dèanamh cinnteach gu bheil an inneal gu dearbh Yealink agus a' tilleadh na luachan riatanach (ip agus mac).

A 'cleachdadh iarrtas sònraichte, bidh sinn a' faighinn a-mach an cunntas làithreach air a 'fòn. Gus seo a dhèanamh, tha an rèiteachadh gnàthach air a luchdachadh sìos bhon fhòn agus air a pharsadh.

def get_phone_inform(ipaddr):
    fileconf = requests.get('http://admin:admin@'+ipaddr+'/servlet?phonecfg=get[&accounts=1]')
    conf = fileconf.text.split('|')
    current_account = conf[2]
    return current_account

Faigh a-mach am facal-faire airson a’ chunntais seo. Gus seo a dhèanamh, tionndaidhidh sinn chun chlàr asterisk.sip agus an raon dàta ann.

def check_account(current_account):
    connect = mysql.connector.connect(host='192.168.0.3', database='asterisk', user='voip_wr', password='***')
    cursor = connect.cursor()
    qwery = 'select data from sip where id=' + current_account + ' and keyword="secret";'
    cursor.execute(qwery)
    password = cursor.fetchall()
    if password == ' ':
        exit()
    else:
        return password

Uill, airson an ìre mu dheireadh bidh sinn a’ ceangal ri ldap AD agus a’ cleachdadh sAMAccountName a gheibhear tron ​​ghnìomh getpass.getuser() gabh cn a’ chleachdaiche gnàthach (anns a bheil ainm slàn an neach-cleachdaidh mar as trice).

def conn_ldap(login):
    ad = ldap.initialize('ldap://***.local')
    ad.simple_bind_s('voip@***.local', 'password')
    basedn = 'OU=***,DC=***,DC=LOCAL'
    basedn_user = 'OU=***,OU=***,DC=***,DC=LOCAL'
    scope = ldap.SCOPE_SUBTREE
    filterexp = "(&(sAMAccountName=" + login + ")(ObjectClass=person))"
    filterexp2 = "(&(ObjectClass=organizationUnit))"
    attrlist = ['cn']
    attrlist2 = ['OU']
    search = ad.search_s(basedn, scope, filterexp, attrlist)
    adname = search[0][1]['cn'][0].decode('utf-8')
    if adname == ' ':
        search = ad.search_s(basedn_user, scope, filterexp2, attrlist2)
        for i in range(1, len(search)+1):
            group = search[i][1]['ou'][0]
            basedn_user2 = 'OU='+group+','+basedn_user
            search = ad.search_s(basedn_user2, scope, filterexp, attrlist)
            adname = search[0][1]['cn'][0].decode('utf-8')
            if adname != ' ':
                return adname
        adname = search[0][1]['cn'][0].decode('utf-8')
    ad.unbind_s()
    return adname

Bidh sinn a’ ceangal ri clàr a chaidh a chruthachadh ro-làimh san stòr-dàta (chruthaich mi an sin e) agus cuir a-steach a h-uile dad a dh’ ionnsaich sinn, is e sin: ip, mac, ainm-cleachdaidh.

def conn_mysql(query,fquery,macaddr,qwery2):
    connect = mysql.connector.connect(host='192.168.0.3', database='voip', user='voip_wr', password='***')
    cursor = connect.cursor()
    cursor.execute(fquery)
    state = cursor.fetchall()
    state = bool(state[0][0])
    if state == True:
        cursor.execute(qwery2)
        connect.commit()
        connect.close()
    else:
        cursor.execute(query)
        connect.commit()
        connect.close()

Dh’ fhaodadh sinn stad an seo, leis gu bheil sinn air leabhar seòlaidhean fiùghantach a chruthachadh mar-thà, is dòcha gu bheil thu a’ faighneachd, ach chaidh mi nas fhaide agus chuir mi solarachadh fèin-ghluasadach de dh’ innealan an seo.

Gus seo a dhèanamh, thèid rèiteachadh teamplaid a luchdachadh sìos bho fhrithealaiche tftp ro-shuidhichte, anns am bi sinn a’ dèanamh ar n-atharrachaidhean agus ga shàbhaladh mar mac.cfg. Is e sin, airson Yealink tha dà sheòrsa rèiteachaidh ann, tha aon dhiubh cruinneil, agus tha an dàrna fear a’ buntainn ri fòn sònraichte agus bu chòir dha a bhith san fhoirm mac_phone.cfg

Às deidh a h-uile atharrachadh san fhaidhle agus ga shàbhaladh air ais don t-seirbheisiche tftp, bheir sinn an àithne don fhòn solarachadh agus ath-thòisich an inneal.

def tftp_file_change(config,place,adname,current_account,current_account_password):

    client = tftpy.TftpClient("192.168.0.3", 69)
    client.download('template.cfg', place)
    fileread = open(place, 'r')
    line = fileread.readlines()
    fileread.close()
    line[5] = (('account.1.label = ').encode('utf-8') + adname.encode('utf-8') + 'n')
    line[2] = (('account.1.auth_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[3] = (('account.1.display_name = ').encode('utf-8') + current_account.encode('utf-8') + 'n')
    line[6] = (('account.1.password = ').encode('utf-8') + current_account_password[0][0] + 'n')
    filewrite = open(place, 'w')
    for i in line:
      filewrite.write(i)
    filewrite.close()
    print place
    print config
    client.upload(config,place)

requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=AutoP')
requests.get('http://admin:admin@'+ipaddr+'/cgi-bin/ConfigManApp.com?key=Reboot')

Às deidh dhuinn an inneal ath-thòiseachadh, gheibh sinn ar n-ainm slàn air scrion a ’fòn + leabhar seòlaidh a tha an-còmhnaidh air a lìonadh gu ceart ann an cruth stòr-dàta, agus an uairsin chan eil air fhàgail ach XML agus beagan PHP a chuir ris gus an susbaint a thaisbeanadh gu dinamach. Tha tòrr eisimpleirean mar sin ann, tha eadhon YEALINK aca fhèin.

PS: Airson barrachd scalability, faodaidh tu na prìomh roghainnean (caochladairean) a ghluasad gu faidhle air leth.

Source: www.habr.com

Cuir beachd ann