summaryrefslogtreecommitdiff
path: root/kpov_judge/tasks/radius_mysql_pam/task.py
blob: a3f86f7f02c858e51eb22ad3b484464aff1f3fa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# kpov_random_helpers should be imported by add_assignment.py

instructions = {
    'si':"""
Ustvari dva navidezna računalnika - SimpleArbiter z diska simpleArbiterDhcp ter
RadiusServer. Na RadiusServer namesti FreeRadius ter MySQL. 

Ustvari mysql podatkovno bazo z imenom {MYSQL_DB_NAME}. Ustvari mysql
uporabnika z imenom {MYSQL_ADMIN_USER} in geslom {MYSQL_ADMIN_PASSWORD},
ki naj ima poln dostop do te baze. Prijava za tega uporabnika mora biti
omogočena tudi s SimpleArbiter.

Nastavi FreeRadius tako, da bo podatke o uporabnikih in geslih pobiral iz baze 
MySQL z imenom {MYSQL_DB_NAME}. Podatkovna shema (imena tabel) naj ostane
privzeta.

Dostop do strežnika Radius na RadiusServer s SimpleArbiter naj bo mogoč ob
uporabi skrivnosti {RADIUS_SECRET}.

V bazi ustvari vnos, ki bo omogočil, da se na RadiusServer s pomočjo protokola
Radius avtenticira uporabnik {RADIUS_USERNAME} z geslom {RADIUS_PASSWORD}.

Nastavi PAM za prijavo (login) tako, da bo dovolj, če se uporabnik na ssh
predstavi z uporabniškim imenom in geslom, ki sta veljavna na FreeRadius,
ne glede na /etc/shadow oziroma /etc/password.
""",
   'en':"""
Create two virtual machines - SimpleArbiter using the disk simpleArbiterDhcp
and RadiusServer. On RadiusServer, install FreeRadius and MySQL.

Create a mysql database named {MYSQL_DB_NAME}. Create a mysql user with the
username {MYSQL_ADMIN_USER} and password {MYSQL_ADMIN_PASSWORD}. Make sure
this user can access the database from SimpleArbiter and has administrative 
rights over the {MYSQL_DB_NAME} database.

Set up FreeRadius so that the data about users and passwords is stored in the
MySQL database. Keep the default schema (table names).

Make the Radius server on RadiusServer accessible from SimpleArbiter using
{RADIUS_SECRET} as the secret.

Create an entry in the database which will enable a user with the username
{RADIUS_USERNAME} to authenticate themself against the Radius server using
the password {RADIUS_PASSWORD}.

Set up PAM to enable login over ssh using a username and password which are
valida on the FreeRadius server, regardless of the entries in /etc/shadow
and/or /etc/password.
"""
}

#KABOOM

computers = {
    'RadiusServer': {
        'disks': [
            {   'name': 'student-RadiusServer',
            },
            #{   'name': 'CDROM',
            #    'options':{'readonly': True},
            #    'parts': [],# no parts, no mounting.
            #}
        ],
        'network_interfaces': [{'network': 'net1'}],
        'flavor': 'm1.tiny',
        'config_drive': False

    },
    'SimpleArbiter': {
        'disks': [
            {   'name': 'simpleArbiterDhcpGW',
                # attempt automount
            },
            #{   'name': 'CDROM',
            #    'options': {'readonly': True},
            #    'parts': [{'dev': 'b1', 'path': '/cdrom'}],
            #},
        ],
        'network_interfaces': [{'network': 'net1'}, {'network': 'test-net'}],
        'flavor': 'm1.tiny',
        'config_drive': False
    }
}

networks = { 'net1': {'public': False}, 'test-net': {'public': True} }

params_meta = {
    'IP_RS': {'descriptions': {'si': 'Naslov RadiusServer', 'en': 'RadiusServer IP address'}, 'w': True, 'public':True, 'type': 'IP', 'generated': False},
    'RADIUS_SECRET':{'descriptions': {'si': 'Skrivnost RADIUS', 'en': 'RADIUS secret'}, 'w': False, 'public':True, 'type': 'password', 'generated': True},
    'RADIUS_USERNAME': {'descriptions': {'si': 'Uporabniško ime', 'en': 'Username'}, 'w': True, 'public':True, 'type': 'username', 'generated': False},
    'RADIUS_PASSWORD': {'descriptions': {'si': 'Geslo uporabnika', 'en': 'Password'}, 'w': False, 'public':True, 'type': None, 'generated': True},
    'MYSQL_DB_NAME': {'descriptions': {'si': 'Ime baze v mysql', 'en': 'Database name'}, 'w': False, 'public':True, 'type': None, 'generated': True},
    'MYSQL_ADMIN_USER':{'descriptions': {'si': 'Uporabniško ime za dostop do MySQL', 'en': 'MySQL username'}, 'w': False, 'public':True, 'type': 'username', 'generated': True},
    'MYSQL_ADMIN_PASSWORD': {'descriptions': {'si': 'Geslo za dostop do MySQL', 'en': 'MySQL password'}, 'w': True, 'public':True, 'type': 'password', 'generated': True},
    'MYSQL_SEED':{'descriptions': {'si': 'seed', 'en': 'seed'}, 'w': False, 'public':True, 'type': None, 'generated': True},
}

def task(IP_RS, RADIUS_SECRET, RADIUS_USERNAME, RADIUS_PASSWORD, MYSQL_DB_NAME, MYSQL_ADMIN_USER, MYSQL_ADMIN_PASSWORD, MYSQL_SEED):
    from pexpect import pxssh
    import pexpect
    import random
    results = dict()
    peer_user = 'student'
    peer_passwd = 'vaje'
    r = random.Random(MYSQL_SEED)
    MYSQL_TEST_USER = kpov_random_helpers.username_gen(r)
    MYSQL_TEST_PASSWORD = kpov_random_helpers.alnum_gen(r, 7)
    RADIUS_NEW_PASSWORD = kpov_random_helpers.alnum_gen(r, 7)
    # Testiranje radius strtežnika
    results['Test_RadiusServer'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format(
        RADIUS_USERNAME, RADIUS_PASSWORD, IP_RS, RADIUS_SECRET))
    # Testiranje podatkovne base mysql
    mysql = pexpect.spawn('mysql -u {MYSQL_ADMIN_USER} -p{MYSQL_ADMIN_PASSWORD} -h {IP_RS}'.format(**locals()))
    mysql.expect("mysql>")
    results['mysql_login'] = mysql.before
    mysql.sendline('USE {MYSQL_DB_NAME}'.format(**locals()))	
    mysql.expect("mysql>")
    results['database_connect'] = mysql.before
    mysql.sendline('SELECT UserName, Value FROM radcheck;')
    mysql.expect("mysql>")
    results['select_from_users'] = mysql.before
    mysql.sendline("INSERT INTO radcheck (UserName, Attribute, Value, Op) VALUES ('{MYSQL_TEST_USER}', 'Cleartext-Password', '{MYSQL_TEST_PASSWORD}', ':=');".format(**locals()))
    mysql.expect("mysql>")
    results['radtest_OK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format(
        MYSQL_TEST_USER, MYSQL_TEST_PASSWORD, IP_RS, RADIUS_SECRET))
    results['radtest_NOK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format(
        MYSQL_TEST_USER, "Flügzeug", IP_RS, RADIUS_SECRET))
    results['radtest_NOK'] = pexpect.run('radtest {0} {1} {2} 1812 {3}'.format(
        MYSQL_TEST_USER, "Flügzeug", IP_RS, RADIUS_SECRET))
    sT = pxssh.pxssh()
    mysql.sendline("UPDATE radcheck SET value='{RADIUS_NEW_PASSWORD}' where UserName='{RADIUS_USERNAME}' and Attribute='Cleartext-Password';".format(**locals()))
    sT.login(IP_RS, RADIUS_USERNAME, RADIUS_NEW_PASSWORD)
    results['login_test'] = sT.before
    mysql.sendline("UPDATE radcheck SET value='{RADIUS_PASSWORD}' where UserName='{RADIUS_USERNAME}' and Attribute='Cleartext-Password';".format(**locals()))
    mysql.expect('mysql>')
    mysql.sendline("DELETE FROM radcheck where UserName='{MYSQL_TEST_USER}' and Attribute='Cleartext-Password';".format(**locals()))
    mysql.expect('mysql>')
    mysql.sendline('\q');
    # Testiranje PAM s testnim uporabnikom
    return results
    
def gen_params(user_id, params_meta):
    params = dict()
    r = random.Random(user_id)
    params['RADIUS_SECRET'] = kpov_random_helpers.alnum_gen(r, 8)
    params['RADIUS_PASSWORD'] = kpov_random_helpers.alnum_gen(r, 8)
    params['RADIUS_USERNAME'] = kpov_random_helpers.username_gen(r)
    params['MYSQL_ADMIN_USER'] = kpov_random_helpers.alnum_gen(r, 6)
    params['MYSQL_ADMIN_PASSWORD'] = kpov_random_helpers.alnum_gen(r, 6)
    params['MYSQL_DB_NAME'] = kpov_random_helpers.alnum_gen(r, 4)
    params['MYSQL_SEED'] = str(r.random())
    return params

def task_check(results, params):
    import re
    import pickle
    score = 0
    hints = []
    r = random.Random(params['MYSQL_SEED'])
    MYSQL_TEST_USER = kpov_random_helpers.username_gen(r)
    MYSQL_TEST_PASSWORD = kpov_random_helpers.alnum_gen(r, 7)
    RADIUS_NEW_PASSWORD = kpov_random_helpers.alnum_gen(r, 7)
    s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {IP_RS}:1812 length [0-9]+\r\n\tUser-Name = \"{RADIUS_USERNAME}\"\r\n\tUser-Password = \"{RADIUS_PASSWORD}\".*Access-Accept Id [0-9]+ from {IP_RS}".format(**params)
    #with open('test.pickle', 'w') as f:
    #    pickle.dump({'pattern': s, 'res': results['Test_RadiusServer']}, f)
    if re.search(s, results['Test_RadiusServer'], flags=re.DOTALL):
        # print "Test OK"
        score += 2
    else:
        hints.append('radtest connect output incorrect:' + results['Test_RadiusServer'])
        print((results['Test_RadiusServer'], s))
    # Testiranje podatkovne base mysql
    s = "Welcome to the MySQL monitor.*Type 'help;' or '\\\\h' for help\\. Type '\\\\c' to clear the current input statement\\.\r\n"
    if re.search(s, results['mysql_login'], flags=re.DOTALL):
        # print "mysql_login OK"
        score += 1
    else:
        hints.append("mysql connection string incorrect")
        print((results['mysql_login'], s))
    s = " USE {MYSQL_DB_NAME}\r\nReading table information.*Database changed\r\n".format(**params)
    if re.search(s, results['database_connect'], flags=re.DOTALL):
        # print "database_connect OK"
        score += 1
    else:
        hints.append('mysql table information string incorrect')
        print((results['database_connect'],))
    s = " SELECT UserName, Value FROM radcheck;\r\n.*{RADIUS_USERNAME} *| *{RADIUS_PASSWORD}".format(**params)
    if re.search(s, results['select_from_users'], flags=re.DOTALL):
        # print "select_from_users OK"
        score += 2
    else:
        hints.append('mysql user entry in table check failed')
        print((results['select_from_users'], ))

    s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {0}:1812 length [0-9]+\r\n\tUser-Name = \"{1}\"\r\n\tUser-Password = \"{2}\".*Access-Accept Id [0-9]+ from {0}".format(params['IP_RS'], MYSQL_TEST_USER, MYSQL_TEST_PASSWORD)
    if re.search(s, results['radtest_OK'], flags=re.DOTALL):
        # print "radtest_OK OK"
        score += 2
    else:
        hints.append('radtest output incorrect:' + results['radtest_OK'])
        print((s, results['radtest_OK']))

    s = r"Sent Access-Request Id [0-9]+ from ([0-9]|\.)+:[0-9]+ to {0}:1812 length [0-9]+\r\n\tUser-Name = \"{1}\"\r\n\tUser-Password = \"Flügzeug\".*Access-Reject Id [0-9]+ from {0}".format(params['IP_RS'], MYSQL_TEST_USER)
    if re.search(s, results['radtest_NOK'], flags=re.DOTALL):
        # print "radtest_NOK OK"
        score += 1
    else:
        hints.append('radtest negative output incorrect: ' + results['radtest_NOK'])
        print((results['radtest_NOK'], s))
    s = "{RADIUS_USERNAME}@.*:~\\$".format(**params)
    if re.search(s, results['login_test'], flags=re.DOTALL):
        # print "login_test OK"
        score += 1
    else:
        hints.append('login test failed')
        print((results['login_test'],s))
    return score, hints

def prepare_disks(templates, task_params, global_params):
    write_default_config(templates['simpleArbiterDhcpGW'], global_params)