Projet

Général

Profil

connexions.py

Fichier de debug (crée un log dans /tmp) - Klaas TJEBBES, 19/05/2017 10:15

Télécharger (19,7 ko)

 
1
# -*- coding: UTF-8 -*-
2
###########################################################################
3
# Eole NG - 2007
4
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
5
# Licence CeCill  cf /root/LicenceEole.txt
6
# eole@ac-dijon.fr
7
# modifié par Cadoles 2012
8
#
9
# connectes.py
10
#
11
# Librairie de gestion des connectés
12
#
13
###########################################################################
14

    
15
import MySQLdb
16
import sys, os, tdb, time, glob, re #, struct
17
sys.path.append('/usr/share/eole/controlevnc')
18

    
19
from scribe.login import Ldap, log_connexion_db
20
from scribe.ldapconf import USER_FILTER
21
from pyeole.diagnose import test_tcp
22
from config import mysql_password, mysql_host, master_ip
23

    
24
tdb_file = '/var/run/samba/smbXsrv_session_global.tdb'
25
separator = '\x00'
26
PROC_TCP = "/proc/net/tcp"
27
TDB_HANDLER = tdb.Tdb(tdb_file, os.O_RDONLY)
28

    
29
def remove_duplicated_user_for_ip(sockets):
30
    last_users = {}
31
    def _get_timestamp_of_inode(inode):
32
        '''
33
        To retrieve the process's creation timestamp, check every running process and look for one using
34
        the given inode.
35
        '''
36
        for item in glob.glob('/proc/[0-9]*/fd/[0-9]*'):
37
            try:
38
                if re.search(inode, os.readlink(item)):
39
                    return os.stat(os.path.dirname(item)).st_ctime
40
            except:
41
                pass
42

    
43
    def _hex2dec(s):
44
        return str(int(s,16))
45

    
46
    def _ip(s):
47
        ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))]
48
        return '.'.join(ip)
49

    
50
    def _convert_ip_port(array):
51
        host,port = array.split(':')
52
        return 'ipv4:'+_ip(host)+':'+_hex2dec(port)
53

    
54
    with open(PROC_TCP,'r') as f:
55
        all_connexions = f.readlines()
56
        all_connexions.pop(0)
57

    
58
    duplicated = {}
59
    for ip, lst in sockets.items():
60
        if len(lst) != 1:
61
            for ls in lst:
62
                duplicated[ls[0]] = (ip, ls[1])
63
        else:
64
            last_users[ip] = (lst[0][1], ip)
65

    
66
    if duplicated != {}:
67
        _last_users = {}
68
        for line in all_connexions:
69
            line_array = [x for x in line.split(' ') if x !='']    # Split lines and remove empty spaces.
70
            comp_socket =  _convert_ip_port(line_array[2])
71
            # only ESTABLISHMENT and corresponding socket
72
            if line_array[3] == '01' and comp_socket in duplicated.keys():
73
                timestamp = _get_timestamp_of_inode(line_array[9])
74
                ip, username = duplicated[comp_socket]
75
                # si l'ip n'a pas encore été traité ou si l'ip est traité mais avec une date plus ancienne
76
                # c'est le nouveau username qui est associé à l'IP
77
                if ip not in _last_users or _last_users[ip][2] < timestamp:
78
                    _last_users[ip] = (username, ip, timestamp)
79
        for ip, _last_user in _last_users.items():
80
            last_users[ip] = (_last_user[0], _last_user[1])
81

    
82
    return last_users.values()
83

    
84
class ComputerNameError(Exception):
85
    pass
86

    
87

    
88
class Session:
89
    def __init__(self, only=None):
90
        """lit le contenu de sessionid.tdb
91
        only: get session information only for one IP
92
        """
93
        cnt = 0
94
        max_cnt = 10
95
        for cnt in range(max_cnt):
96
            try:
97
                self._init_sessions(only)
98
                break
99
            except:
100
                TDB_HANDLER.reopen()
101
                if cnt == max_cnt-1:
102
                    # pour voir ce qui plante
103
                    self._init_sessions(only)
104
                time.sleep(0.2)
105

    
106
    def _init_sessions(self, only):
107
        self.sessions = []
108
        sessions = {}
109
        for key in TDB_HANDLER.iterkeys():
110
            #key must endswith \x00
111
            data = TDB_HANDLER.get(key)
112
            if data is None:
113
                continue
114

    
115
            #(tid, ) = struct.unpack("<L", data[4:8])
116
            sdata = data.split(separator)
117
            #parse les colonnes de tdb a la recherche du nom de la socket
118
            #l'IP est utilisé dans la socket
119
            for col in sdata:
120
                col = str(col)
121
                if col[:5] == 'ipv4:' and col[-4:] != ':445':
122
                    socket = col
123
                    ip = socket[5:].split(':')[0]
124
                    break
125
            #le nom d'utilisateur est dans la dernière colonne
126
            if only is not None:
127
                if ip != only:
128
                    continue
129
            username = sdata[-2]
130
            if username == '':
131
                continue
132
            if self.is_user_account(username):
133
                sessions.setdefault(ip, []).append((socket, username))
134
        self.sessions = remove_duplicated_user_for_ip(sessions)
135

    
136
    def is_user_account(self, nom):
137
        if not nom.endswith('$'):
138
            ldap_conn = Ldap()
139
            if ldap_conn.get_user_attributs('%s'%nom) != {}:
140
                return True
141

    
142
    def get_users_by_ip(self, ip):
143
        """renvoi users par ip
144
        """
145
        ret = []
146
        for uid, _ip in self.sessions:
147
            if _ip == ip:
148
                ret.append(uid)
149
        return ret
150

    
151
    def get_sessions_by_ip(self, ip):
152
        """renvoi les sessions d'une IP
153
        """
154
        ret = []
155
        for uid, _ip in self.sessions:
156
            if _ip == ip:
157
                ret.append((uid, _ip))
158
        return ret
159

    
160
    def has_session(self, ip):
161
        for uid, _ip in self.sessions:
162
            if _ip == ip:
163
                return ip
164

    
165
    def is_connected(self, user):
166
        for _user, _ in self.sessions:
167
            if _user == user:
168
                return user
169

    
170
    def get_ip(self, user):
171
        """retourne l'ip de la (des) station(s) ou est connecte <user>
172
        """
173
        res = []
174
        for _user, _ip in self.sessions:
175
            if _user == user:
176
                res.append(_ip)
177
        return res
178

    
179
class Connexions:
180
    def __init__(self):
181
        self.db = MySQLdb.connect(host=mysql_host, user='controlevnc',
182
                passwd=mysql_password, db='controlevnc')
183
        self.cursor = self.db.cursor()
184
        self.sessions = None
185

    
186
    def __del__(self):
187
        self.db.commit()
188
        self.db.close()
189

    
190
    def service_start(self, ip, mac):
191
        """met True à up dans la base de donnée pour l'IP correspondante
192
        """
193
        self.cursor.execute("SELECT up FROM log WHERE ip='%s'" % ip)
194
        row = self.cursor.fetchone()
195
        if row:
196
            if row[0] != True:
197
                #si l'ip est déjà dans la base n'est pas up=True
198
                self.cursor.execute("UPDATE log SET up=True, mac='%s' WHERE ip='%s'" % (mac, ip))
199
        else:
200
            #si l'IP n'existe pas dans la base, creer la machine
201
            self.cursor.execute("INSERT INTO log (ip, up, mac) VALUES ('%s', True, '%s')" \
202
                    % (ip, mac))
203
        return True
204

    
205
    def service_stop(self, ip):
206
        """met False à up dans la base de donnée pour l'IP correspondante
207
        """
208
        self.cursor.execute("SELECT up FROM log WHERE ip='%s'" % ip)
209
        row = self.cursor.fetchone()
210
        if row:
211
            if row[0] != False:
212
                #si l'ip est déjà dans la base n'est pas up=False
213
                self.cursor.execute("UPDATE log SET up=False WHERE ip='%s'"%ip)
214
        return True
215

    
216
    def get_stations(self):
217
        """
218
        retourne la liste des machines dans la base de donnée des machines
219
        """
220
        self.cursor.execute("SELECT netbios, ip, mac FROM log")
221
        ret = []
222
        for row in self.cursor.fetchall():
223
            ret.append(','.join(row))
224
        return ';'.join(ret)
225

    
226
    def get_station_up(self, verify):
227
        """
228
        retourne la liste des machines dans la base de donnée des machines
229
        avec session ouverte (information du Client Scribe).
230
        Si verify (True|False):
231
        - comparaison avec la liste des sessions samba ;
232
        - test si la présence du port 8788 (Client Scribe), dans ce cas le nom
233
        de l'utilisation n'est pas mentionné).
234
        """
235
        #FIXME : devrait ajouter les machines de la session nom présente dans
236
        #bdd
237
        self.cursor.execute("SELECT ip, netbios, user FROM log WHERE up=True")
238
        ret = []
239
        session_ips = [ip for user, ip in self.get_sessions().sessions]
240
        for row in self.cursor.fetchall():
241
            #si le poste a une session samba ou est accessible sur le port 8788
242
            if not verify or row[0] in session_ips:
243
                ret.append(', '.join(row))
244
            elif test_tcp(row[0], 8788):
245
                ret.append("%s, %s," % (row[0], row[1]))
246
        return ';'.join(ret)
247

    
248
    def get_connected_by_group(self, group):
249
        """voir les connecter pour un groupe"""
250
        sql = "SELECT ip, user, os, prim_group, netbios FROM log WHERE up=True and ip IN "
251
        sql += "(SELECT ip FROM `group` WHERE groupname='%s');" % group
252
        self.cursor.execute(sql)
253
        members = []
254
        ip_infos = {}
255
        for row in self.cursor.fetchall():
256
            ip, user, os, prim_group, netbios = row
257
            members.append(user)
258
            ip_infos[ip] = (os, prim_group, netbios)
259

    
260
        sessions = self.get_sessions()
261
        liste = []
262
        #FIXME : supprimer les comptes en trop dans la bdd !
263
        for username, ip in sessions.sessions:
264
            try:
265
                if username not in members:
266
                    continue
267
                os, prigrp, netbios = ip_infos[ip]
268

    
269
                liste.append((username, prigrp, netbios, os, ip))
270
            except:
271
                continue
272
        liste.sort()
273
        return liste
274

    
275
    def get_connected_student(self, group=None):
276
        """renvoie la liste des connectes pour n'avoir que les élèves du
277
        groupe [USERNAME]
278
        """
279
        # retourne que les eleves
280
        prigrp = "eleves"
281
        # récupération des connectés
282
        #croise parce que cherche prigrp eleves + groupe + connecté
283
        """retourne les utilisateurs présent dans 2 groupes"""
284
        sql = "SELECT ip, os, user, netbios FROM log WHERE up=True and ip IN "
285
        if group is None:
286
            sql += "(SELECT ip FROM `group` WHERE groupname='%s')" % prigrp
287
        else:
288
            sql += "(SELECT ip FROM `group` WHERE EXISTS"
289
            sql += "(SELECT ip FROM `group` WHERE groupname='%s')" % prigrp
290
            sql += "AND groupname='%s');" % group
291
        self.cursor.execute(sql)
292
        membres = []
293
        ip_infos = {}
294
        for row in self.cursor.fetchall():
295
            ip, os, user, netbios = row
296
            membres.append(user)
297
            ip_infos[ip] = (os, netbios)
298
        # le groupe est vide
299
        if not membres:
300
            return []
301
        sessions = self.get_sessions()
302
        liste = []
303
        for user, ip in sessions.sessions:
304
            try:
305
                # uid, grp, machine, os(détecté), ip
306
                if user not in membres:
307
                    continue
308
                os, nom_mach = ip_infos[ip]
309

    
310
                liste.append((user, prigrp, nom_mach, os, ip))
311
            except:
312
                continue
313
        liste.sort()
314
        return liste
315

    
316
    def get_os_from_ip(self, ip):
317
        sql = "SELECT os FROM log WHERE ip='%s'" % ip
318
        self.cursor.execute(sql)
319
        row = self.cursor.fetchone()
320
        if row:
321
            return row[0]
322

    
323
    def get_ip_from_netbios(self, netbios):
324
        sql = "SELECT ip FROM log WHERE netbios='%s'" % netbios
325
        self.cursor.execute(sql)
326
        row = self.cursor.fetchone()
327
        if row:
328
            return row[0]
329

    
330
    def get_netbios_from_ip(self, ip):
331
        sql = "SELECT netbios FROM log WHERE ip='%s'" % ip
332
        self.cursor.execute(sql)
333
        row = self.cursor.fetchone()
334
        if row:
335
            return row[0]
336

    
337
    def get_mac_from_ip(self, ip):
338
        sql = "SELECT mac FROM log WHERE ip='%s'" % ip
339
        self.cursor.execute(sql)
340
        row = self.cursor.fetchone()
341
        if row:
342
            return row[0]
343

    
344
    def get_netbios(self):
345
        sql = "SELECT netbios FROM log"
346
        self.cursor.execute(sql)
347
        ret = []
348
        row = self.cursor.fetchall()
349
        if row:
350
            for r in row:
351
                ret.append(r[0])
352
        return ret
353

    
354
    def get_sid(self, user):
355
        #FIXME manque UP
356
        sql = "SELECT sid FROM log WHERE user='%s'" % user
357
        self.cursor.execute(sql)
358
        row = self.cursor.fetchone()
359
        if row:
360
            return row[0]
361
        #si pas dans la base de donnée
362
        return Ldap().ldap_search("(&%s(uid=%s))" % (USER_FILTER, user),
363
                ['sambaSID'], True)['sambaSID'][0]
364

    
365
    def get_sessions(self, only=None):
366
        if not self.sessions:
367
            self.sessions = Session(only)
368
        return self.sessions
369

    
370
    def writedbg(self, msg):
371
        debuglogfile = '/tmp/controlevnc-debug.log'
372
        try:
373
            with open(debuglogfile, 'a') as dbgfile:
374
                dbgfile.write('%s %s\n'%(time.strftime('%Y/%m/%d %H:%M:%S'), msg))
375
        except: pass
376

    
377
    def get_infos(self, ip, os_type):
378
        try: self.writedbg('####################################### get_infos')
379
        except: pass
380
        sessions = self.get_sessions(only=ip)
381
        try: self.writedbg('%s session : %s'%(ip, sessions))
382
        except: pass
383
        users = sessions.get_users_by_ip(ip)
384
        try: self.writedbg('%s users : %s'%(ip, users))
385
        except: pass
386
        #récupération des informations sur l'utilisateur dans la base de données
387
        #FIXME manque UP
388
        sql = "SELECT user, sid, display_name, os, netbios FROM log "
389
        sql += "WHERE ip='%s'" % ip
390
        self.cursor.execute(sql)
391
        row = self.cursor.fetchone()
392
        try: self.writedbg('%s row : %s'%(ip, row))
393
        except: pass
394
        try: self.writedbg('%s rowlist : %s'%(ip, [i for i in self.cursor]))
395
        except: pass
396
        if row:
397
            # Si personne dans la session
398
            # ou un et un seul utilisateur identique à la base de donnée
399
            # ou l'utilisateur de la base est dans la liste des sessions
400
            # (il arrive que net status sessions renvoi plusieurs utilisateurs
401
            # retourne les valeurs de la base
402
            # et si _os n'est pas vide
403
            user, sid, display_name, _os, netbios = row
404
            if users == [] or (len(users) == 1 and users[0] == user) or \
405
                    (len(users) > 1 and user in users) and _os:
406
                groups = self._get_sql_groups(ip)
407
                return user, sid, display_name, groups, _os, netbios
408
        try: self.writedbg('%s else row : %s'%(ip, row))
409
        except: pass
410
        # si pas d'utilisateur dans la base de donnée
411
        # et dans net sessions status
412
        if users == []:
413
            raise Exception("Personne ne s'est connecte sur %s"%ip)
414
        # utilisateur de la base et connecté différent
415
        # ou pas d'utilisateur dans la base de donnée
416
        # fait confiance à net status sessions
417
        # on récupère les informations dans LDAP
418
        user = users[-1]
419

    
420
        ldap_conn = Ldap()
421
        user_attrib = ldap_conn.get_user_attributs(user)
422
        try:
423
            sid = user_attrib['sambaSID'][0].strip()
424
        except:
425
            raise Exception('utilisateur inconnu {0}, les informations de la session sont {1}'.format(user, sessions.sessions))
426
        display_name = user_attrib['displayName'][0].strip()
427
        gidnumber = user_attrib['gidNumber'][0].strip()
428
        groups = []
429
        for dn, grp in ldap_conn.get_groups(user):
430
            name = grp['cn'][0]
431
            if grp['gidNumber'][0] == gidnumber:
432
                prim_group = name
433
            groups.append(name)
434
        del(ldap_conn)
435
        netbios = "" # sessions.get_netbios(ip) => le netbios n'est plus disponible dans l'objet session
436

    
437
        try: self.writedbg('%s LDAP : %s'%(ip, [user, sid, display_name, prim_group, groups, netbios, os_type, ip]))
438
        except: pass
439
        # met à jour la base de donnée
440
        log_connexion_db(user, sid, display_name, prim_group, groups, netbios,
441
                os_type, ip)
442

    
443
        return user, sid, display_name, groups, os_type, netbios
444

    
445
    def _get_sql_groups(self, ip):
446
        """retourne les groupes de l'utilisateur connecté à une IP sans
447
        vérifier l'utilisateur"""
448
        sql = "SELECT groupname FROM `group` WHERE ip='%s'" % ip
449
        self.cursor.execute(sql)
450
        groups = [group[0] for group in self.cursor.fetchall()]
451
        return groups
452

    
453
    def get_groups(self, ip):
454
        """retourne les groupes de l'utilisateur connecter sur IP
455
        """
456
        sessions = self.get_sessions(ip)
457
        users = sessions.get_users_by_ip(ip)
458

    
459
        #récupération des informations sur l'utilisateur dans la base de données
460
        sql = "SELECT user FROM log WHERE up=True AND ip='%s'" % ip
461
        self.cursor.execute(sql)
462
        user = self.cursor.fetchone()
463
        if user:
464
            # Si personne dans la session
465
            # ou un et un seul utilisateur identique à la base de donnée
466
            # ou l'utilisateur de la base est dans la liste des sessions
467
            # (il arrive que net status sessions renvoi plusieurs utilisateurs
468
            # retourne les valeurs de la base
469
            if users == [] or (len(users) == 1 and users[0] == user) or \
470
                    (len(users) > 1 and user in users):
471
                #si pas vide
472
                return self._get_sql_groups(ip)
473

    
474
        # si pas d'utilisateur dans la base de donnée
475
        # et dans net sessions status
476
        if users == []:
477
            raise Exception("Personne ne s'est connecte sur %s"%ip)
478
        # utilisateur de la base et connecté différent
479
        # ou pas d'utilisateur dans la base de donnée
480
        # fait confiance à net status sessions
481
        # on récupère les informations dans LDAP
482
        user = users[-1]
483

    
484
        ldap_conn = Ldap()
485
        groups = []
486
        for dn, grp in ldap_conn.get_groups(user):
487
            groups.append(grp['cn'][0])
488
        del(ldap_conn)
489

    
490
        return groups
491

    
492
    def get_user_from_ip(self, ip):
493
        """retourne l'utilisateur connecté sur ip (#13163)
494
        """
495
        return self.get_user(ip)
496

    
497
    def get_user(self, ip):
498
        """retourne l'utilisateur connecter sur IP
499
        """
500
        sessions = self.get_sessions(ip)
501
        users = sessions.get_users_by_ip(ip)
502

    
503
        #récupération des informations sur l'utilisateur dans la base de données
504
        sql = "SELECT user FROM log WHERE up=True AND ip='%s'" % ip
505
        self.cursor.execute(sql)
506
        row = self.cursor.fetchone()
507
        if row:
508
            user = row
509
            # Si personne dans la session
510
            # ou un et un seul utilisateur identique à la base de donnée
511
            # ou l'utilisateur de la base est dans la liste des sessions
512
            # (il arrive que net status sessions renvoi plusieurs utilisateurs
513
            # retourne les valeurs de la base
514
            if users == [] or (len(users) == 1 and users[0] == user) or \
515
                    (len(users) > 1 and user in users):
516
                #si pas vide
517
                return user
518

    
519
        # si pas d'utilisateur dans la base de donnée
520
        # et dans net sessions status
521
        if users == []:
522
            raise Exception("Personne ne s'est connecte sur %s"%ip)
523
        # utilisateur de la base et connecté différent
524
        # ou pas d'utilisateur dans la base de donnée
525
        # fait confiance à net status sessions
526
        # on récupère les informations dans LDAP
527
        return users[-1]
528

    
529
    def isprof(self, ip):
530
        """renvoie True si l'utilisateur connecté sur <ip> est membre de
531
        "professeurs"
532
        """
533
        if ip == master_ip:
534
            return True
535
        return 'professeurs' in self.get_groups(ip)
536

    
537
    def isadmin(self, ip):
538
        """renvoie True si l'utilisateur connecté sur <ip> est membre de
539
        "DomainAdmins"
540
        """
541
        if ip == master_ip:
542
            return True
543
        return 'DomainAdmins' in self.get_groups(ip)
544

    
545
    def has_session(self, ip):
546
        sessions = self.get_sessions(ip)
547
        return sessions.has_session(ip)
548

    
549
    def is_connected(self, user):
550
        sessions = self.get_sessions()
551
        return sessions.is_connected(user) == user