summaryrefslogtreecommitdiff
path: root/tasks/radius_mysql_pam/task.py
blob: 5051bb427b35ffcf773a27b7478885b0425a9a6a (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
# kpov_util should be imported by add_assignment.py

instructions = {
    'si': '''\
<p>
Ustvari dva navidezna računalnika: <em>SimpleArbiter</em> in <em>RadiusServer</em>. Na <em>RadiusServer</em> namesti FreeRadius ter MySQL.

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

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

<p>
Dostop do strežnika Radius na <em>RadiusServer</em> s <em>SimpleArbiter</em> naj bo mogoč ob uporabi skrivnosti <code>{{RADIUS_SECRET}}</code>.

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

<p>
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 <code>/etc/shadow</code> oziroma <code>/etc/password</code>.
''',
   'en': '''\
<p>
Create two virtual machines: <em>SimpleArbiter</em> and <em>RadiusServer</em>. On <em>RadiusServer</em>, install FreeRadius and MySQL.

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

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

<p>
Make the Radius server on <em>RadiusServer</em> accessible from <em>SimpleArbiter</em> using <code>{{RADIUS_SECRET}}</code> as the secret.

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

<p>
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 <code>/etc/shadow</code>
and/or <code>/etc/password</code>.
''',
}

#KABOOM

computers = {
    'RadiusServer': {
        'disks': [
            {   'name': 'student-RadiusServer',
            },
        ],
        'network_interfaces': [{'network': 'net1'}],
        'flavor': 'm1.tiny',
        'config_drive': False

    },
    'SimpleArbiter': {
        'disks': [
            {   'name': 'simpleArbiterDhcpGW',
            },
        ],
        '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):
    import collections
    import random
    import pexpect

    r = random.Random(MYSQL_SEED)
    MYSQL_TEST_USER = kpov_util.username_gen(r)
    MYSQL_TEST_PASSWORD = kpov_util.alnum_gen(r, 7)
    RADIUS_NEW_PASSWORD = kpov_util.alnum_gen(r, 7)

    results = collections.defaultdict(str)

    # Testiranje radius strež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))

    mysql.sendline("UPDATE radcheck SET value='{RADIUS_NEW_PASSWORD}' where UserName='{RADIUS_USERNAME}' and Attribute='Cleartext-Password';".format(**locals()))

    results.update(kpov_util.ssh_test(IP_RS, RADIUS_USERNAME, RADIUS_NEW_PASSWORD))

    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');
    # TODO 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_util.alnum_gen(r, 8)
    params['RADIUS_PASSWORD'] = kpov_util.alnum_gen(r, 8)
    params['RADIUS_USERNAME'] = kpov_util.username_gen(r)
    params['MYSQL_ADMIN_USER'] = kpov_util.alnum_gen(r, 6)
    params['MYSQL_ADMIN_PASSWORD'] = kpov_util.alnum_gen(r, 6)
    params['MYSQL_DB_NAME'] = kpov_util.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_util.username_gen(r)
    MYSQL_TEST_PASSWORD = kpov_util.alnum_gen(r, 7)
    RADIUS_NEW_PASSWORD = kpov_util.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['motd'], 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)