~jdstrand/click-reviewers-tools/migrate-from-skill-to-interfaces

« back to all changes in this revision

Viewing changes to clickreviews/sr_security.py

  • Committer: Jamie Strandboge
  • Date: 2016-02-24 18:22:42 UTC
  • Revision ID: jamie@ubuntu.com-20160224182242-3pzpwbbuy14az7s0
migrate skills to interfaces

Show diffs side-by-side

added added

removed removed

Lines of Context:
71
71
        '''
72
72
        sec = {}
73
73
 
74
 
        if 'uses' in self.snap_yaml:
75
 
            sec['uses'] = {}
76
 
            # TODO: need to adjust for native security skills
77
 
            for slot in self.snap_yaml['uses']:
78
 
                if 'type' not in self.snap_yaml['uses'][slot] or \
79
 
                        self.snap_yaml['uses'][slot]['type'] != \
80
 
                        'migration-skill':
 
74
        if 'slots' in self.snap_yaml:
 
75
            sec['slots'] = {}
 
76
            # TODO: need to adjust for native security interfaces
 
77
            for slot in self.snap_yaml['slots']:
 
78
                if 'interface' not in self.snap_yaml['slots'][slot] or \
 
79
                        self.snap_yaml['slots'][slot]['interface'] != \
 
80
                        'old-security':
81
81
                    continue
82
 
                for k in self.skill_types['migration-skill']:
83
 
                    if k in self.snap_yaml['uses'][slot]:
 
82
                for k in self.interfaces['old-security']:
 
83
                    if k in self.snap_yaml['slots'][slot]:
84
84
                        # This check means we don't have to verify in the
85
85
                        # individual tests
86
 
                        if not isinstance(self.snap_yaml['uses'][slot][k],
87
 
                                          type(self.skill_types['migration-skill'][k])):
88
 
                            error("Invalid yaml for uses/%s/%s" % (slot, k))  # pragma: nocover
89
 
                        if slot not in sec['uses']:
90
 
                            sec['uses'][slot] = {}
91
 
                        sec['uses'][slot][k] = self.snap_yaml['uses'][slot][k]
 
86
                        if not isinstance(self.snap_yaml['slots'][slot][k],
 
87
                                          type(self.interfaces['old-security'][k])):
 
88
                            error("Invalid yaml for slots/%s/%s" % (slot, k))  # pragma: nocover
 
89
                        if slot not in sec['slots']:
 
90
                            sec['slots'][slot] = {}
 
91
                        sec['slots'][slot][k] = self.snap_yaml['slots'][slot][k]
92
92
 
93
93
        if 'apps' in self.snap_yaml:
94
94
            sec['apps'] = {}
95
95
            for app in self.snap_yaml['apps']:
96
 
                if 'uses' not in self.snap_yaml['apps'][app]:
 
96
                if 'slots' not in self.snap_yaml['apps'][app]:
97
97
                    continue
98
98
                # This check means we don't have to verify in the individual
99
99
                # tests
100
 
                elif not isinstance(self.snap_yaml['apps'][app]['uses'], list):
101
 
                    error("Invalid yaml for %s/uses" % app)  # pragma: nocover
 
100
                elif not isinstance(self.snap_yaml['apps'][app]['slots'], list):
 
101
                    error("Invalid yaml for %s/slots" % app)  # pragma: nocover
102
102
                if app not in sec['apps']:
103
103
                    sec['apps'][app] = {}
104
 
                sec['apps'][app]['uses'] = self.snap_yaml['apps'][app]['uses']
 
104
                sec['apps'][app]['slots'] = self.snap_yaml['apps'][app]['slots']
105
105
 
106
106
        return sec
107
107
 
108
108
    def _extract_security_profile(self, slot, key):
109
109
        '''Extract security profile'''
110
 
        rel_fn = self.policies['uses'][slot]['security-policy'][key]
 
110
        rel_fn = self.policies['slots'][slot]['security-policy'][key]
111
111
 
112
112
        fn = os.path.join(self.unpack_dir, rel_fn)
113
113
        if not os.path.exists(fn):
125
125
        '''Get 'security-policy' policies'''
126
126
        raw_profiles = {}
127
127
 
128
 
        if 'uses' not in self.policies:
 
128
        if 'slots' not in self.policies:
129
129
            return raw_profiles
130
130
 
131
 
        for slot in self.policies['uses']:
132
 
            if 'security-policy' not in self.policies['uses'][slot]:
 
131
        for slot in self.policies['slots']:
 
132
            if 'security-policy' not in self.policies['slots'][slot]:
133
133
                continue
134
134
 
135
135
            if slot not in raw_profiles:
136
136
                raw_profiles[slot] = {}
137
137
 
138
138
            for k in ['apparmor', 'seccomp']:
139
 
                if k in self.policies['uses'][slot]['security-policy']:
 
139
                if k in self.policies['slots'][slot]['security-policy']:
140
140
                    raw_profiles[slot][k] = \
141
141
                        self._extract_security_profile(slot, k)
142
142
 
170
170
 
171
171
    def check_security_caps(self):
172
172
        '''Check security-caps'''
173
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
173
        if not self.is_snap2 or 'slots' not in self.policies:
174
174
            return
175
175
 
176
176
        caps = self._get_policy_groups(version=self.policy_version,
184
184
            # frameworks may reference their own caps
185
185
            frameworks.append(self.snap_yaml['name'])
186
186
 
187
 
        for slot in self.policies['uses']:
188
 
            if 'caps' not in self.policies['uses'][slot]:
 
187
        for slot in self.policies['slots']:
 
188
            if 'caps' not in self.policies['slots'][slot]:
189
189
                continue
190
190
 
191
191
            dupes = []
192
 
            for cap in self.policies['uses'][slot]['caps']:
 
192
            for cap in self.policies['slots'][slot]['caps']:
193
193
                # TODO: this will go away when frameworks are gone
194
194
                framework_cap = False
195
195
                for f in frameworks:
204
204
                elif cap not in caps:
205
205
                    t = 'error'
206
206
                    s = "unsupported cap '%s'" % cap
207
 
                elif self.policies['uses'][slot]['caps'].count(cap) > 1 and \
 
207
                elif self.policies['slots'][slot]['caps'].count(cap) > 1 and \
208
208
                        cap not in dupes:
209
209
                    dupes.append(cap)
210
210
                    t = 'error'
237
237
 
238
238
    def check_security_override(self):
239
239
        '''Check security-override'''
240
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
240
        if not self.is_snap2 or 'slots' not in self.policies:
241
241
            return
242
242
 
243
243
        # These regexes are pretty strict, but lets try to guard against
248
248
                          'syscalls': re.compile(VALID_SYSCALL),
249
249
                          }
250
250
 
251
 
        for slot in self.policies['uses']:
 
251
        for slot in self.policies['slots']:
252
252
            key = 'security-override'
253
 
            if key not in self.policies['uses'][slot]:
 
253
            if key not in self.policies['slots'][slot]:
254
254
                continue
255
255
 
256
256
            t = 'info'
257
257
            n = self._get_check_name(key, extra=slot)
258
258
            s = "OK"
259
 
            if len(self.policies['uses'][slot][key].keys()) == 0:
 
259
            if len(self.policies['slots'][slot][key].keys()) == 0:
260
260
                t = 'error'
261
261
                s = "nothing specified in '%s' for '%s'" % (key, slot)
262
262
            else:
263
 
                for f in self.policies['uses'][slot][key].keys():
 
263
                for f in self.policies['slots'][slot][key].keys():
264
264
                    if f not in allowed_fields:
265
265
                        t = 'error'
266
266
                        s = "unknown field '%s' in " % f + \
267
267
                            "'%s' for '%s'" % (key, slot)
268
 
                    elif not isinstance(self.policies['uses'][slot][key][f],
 
268
                    elif not isinstance(self.policies['slots'][slot][key][f],
269
269
                                        list):
270
270
                        t = 'error'
271
271
                        s = "invalid %s entry: %s (not a list)" % \
272
 
                            (f, self.policies['uses'][slot][key][f])
 
272
                            (f, self.policies['slots'][slot][key][f])
273
273
                    else:
274
274
                        errors = []
275
 
                        for v in self.policies['uses'][slot][key][f]:
 
275
                        for v in self.policies['slots'][slot][key][f]:
276
276
                            if not allowed_fields[f].search(v):
277
277
                                errors.append(v)
278
278
                        if len(errors) > 0:
283
283
 
284
284
    def check_security_policy(self):
285
285
        '''Check security-policy'''
286
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
286
        if not self.is_snap2 or 'slots' not in self.policies:
287
287
            return
288
288
 
289
289
        allowed_fields = ['apparmor', 'seccomp']
296
296
        sc_skip_pat = re.compile(r'^(\s*#|\s*$)')
297
297
        sc_valid_pat = re.compile(VALID_SYSCALL)
298
298
 
299
 
        for slot in self.policies['uses']:
 
299
        for slot in self.policies['slots']:
300
300
            key = 'security-policy'
301
 
            if key not in self.policies['uses'][slot]:
 
301
            if key not in self.policies['slots'][slot]:
302
302
                continue
303
303
 
304
304
            t = 'info'
305
305
            n = self._get_check_name(key, extra=slot)
306
306
            s = "OK"
307
 
            for f in self.policies['uses'][slot][key].keys():
 
307
            for f in self.policies['slots'][slot][key].keys():
308
308
                if f not in allowed_fields:
309
309
                    t = 'error'
310
310
                    s = "unknown field '%s' in " % f + \
311
311
                        "'%s' for '%s'" % (key, slot)
312
 
                elif not isinstance(self.policies['uses'][slot][key][f],
 
312
                elif not isinstance(self.policies['slots'][slot][key][f],
313
313
                                    str):
314
314
                    t = 'error'
315
315
                    s = "invalid %s entry: %s (not a str)" % \
316
 
                        (f, self.policies['uses'][slot][key][f])
 
316
                        (f, self.policies['slots'][slot][key][f])
317
317
            self._add_result(t, n, s)
318
318
 
319
319
        for slot in self.raw_profiles:
371
371
 
372
372
    def check_security_template(self):
373
373
        '''Check security-template'''
374
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
374
        if not self.is_snap2 or 'slots' not in self.policies:
375
375
            return
376
376
 
377
377
        templates = self._get_templates(version=self.policy_version,
385
385
            # frameworks may reference their own caps
386
386
            frameworks.append(self.snap_yaml['name'])
387
387
 
388
 
        for slot in self.policies['uses']:
389
 
            if 'security-template' not in self.policies['uses'][slot]:
 
388
        for slot in self.policies['slots']:
 
389
            if 'security-template' not in self.policies['slots'][slot]:
390
390
                continue
391
391
 
392
 
            template = self.policies['uses'][slot]['security-template']
 
392
            template = self.policies['slots'][slot]['security-template']
393
393
 
394
394
            # TODO: this will go away when frameworks are gone
395
395
            framework_template = False
429
429
            self._add_result(t, n, s, manual_review=m)
430
430
 
431
431
    def check_security_combinations(self):
432
 
        '''Verify security yaml uses valid combinations'''
433
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
432
        '''Verify security yaml slots valid combinations'''
 
433
        if not self.is_snap2 or 'slots' not in self.policies:
434
434
            return
435
435
 
436
 
        for slot in self.policies['uses']:
 
436
        for slot in self.policies['slots']:
437
437
            t = 'info'
438
438
            n = self._get_check_name('yaml_combinations', extra=slot)
439
439
            s = "OK"
440
 
            if "security-policy" in self.policies['uses'][slot]:
 
440
            if "security-policy" in self.policies['slots'][slot]:
441
441
                for i in ['security-override', 'security-template', 'caps']:
442
 
                    if i in self.policies['uses'][slot]:
443
 
                        tmp = list(self.policies['uses'][slot].keys())
 
442
                    if i in self.policies['slots'][slot]:
 
443
                        tmp = list(self.policies['slots'][slot].keys())
444
444
                        tmp.remove("security-policy")
445
445
                        t = 'error'
446
446
                        s = "found '%s' with 'security-policy'" % \
457
457
            n = self._get_check_name('yaml_combinations_apps', app=app)
458
458
            s = "OK"
459
459
            has_decl = []
460
 
            for slot_ref in self.policies['apps'][app]['uses']:
461
 
                if slot_ref not in self.policies['uses']:
 
460
            for slot_ref in self.policies['apps'][app]['slots']:
 
461
                if slot_ref not in self.policies['slots']:
462
462
                    continue
463
463
 
464
464
                for i in ['security-override', 'security-template', 'caps',
465
465
                          'security-policy']:
466
 
                    if i in self.policies['uses'][slot_ref] and \
 
466
                    if i in self.policies['slots'][slot_ref] and \
467
467
                            i not in has_decl:
468
468
                        has_decl.append(i)
469
469
 
472
472
                    if i in has_decl:
473
473
                        has_decl.remove("security-policy")
474
474
                        t = 'error'
475
 
                        s = "'%s' uses 'security-policy' with '%s'" % (
 
475
                        s = "'%s' slots 'security-policy' with '%s'" % (
476
476
                            app, ",".join(sorted(has_decl)))
477
477
                        break
478
478
            self._add_result(t, n, s)
479
479
 
480
 
    def check_uses_redflag(self):
481
 
        '''Check uses redflag fields'''
482
 
        if not self.is_snap2 or 'uses' not in self.policies:
 
480
    def check_slots_redflag(self):
 
481
        '''Check slots redflag fields'''
 
482
        if not self.is_snap2 or 'slots' not in self.policies:
483
483
            return
484
484
 
485
 
        for slot in self.policies['uses']:
 
485
        for slot in self.policies['slots']:
486
486
            t = 'info'
487
487
            n = self._get_check_name('redflag_fields', extra=slot)
488
488
            s = 'OK'
489
489
            m = False
490
490
 
491
491
            attrib = None
492
 
            if 'security-override' in self.policies['uses'][slot]:
 
492
            if 'security-override' in self.policies['slots'][slot]:
493
493
                attrib = 'security-override'
494
 
            elif 'security-policy' in self.policies['uses'][slot]:
 
494
            elif 'security-policy' in self.policies['slots'][slot]:
495
495
                attrib = 'security-policy'
496
496
            if attrib:
497
497
                t = 'error'
499
499
                m = True
500
500
            self._add_result(t, n, s, manual_review=m)
501
501
 
502
 
    def check_apps_uses_mapped_migration(self):
503
 
        '''Check apps uses mapped migration skill'''
 
502
    def check_apps_slots_mapped_oldsecurity(self):
 
503
        '''Check apps slots mapped old-security interface'''
504
504
        if not self.is_snap2 or 'apps' not in self.policies:
505
505
            return
506
506
 
507
507
        for app in self.policies['apps']:
508
 
            for slot_ref in self.policies['apps'][app]['uses']:
 
508
            for slot_ref in self.policies['apps'][app]['slots']:
509
509
                t = 'info'
510
 
                n = self._get_check_name("app_uses", app=app, extra=slot_ref)
 
510
                n = self._get_check_name("app_slots", app=app, extra=slot_ref)
511
511
                s = 'OK'
512
512
                if not isinstance(slot_ref, str):
513
513
                    continue  # checked via sr_lint.py
514
 
                elif slot_ref not in self.policies['uses']:
 
514
                elif slot_ref not in self.policies['slots']:
515
515
                    t = 'error'
516
 
                    s = "slot reference '%s' not in toplevel 'uses'" % slot_ref
 
516
                    s = "slot reference '%s' not in toplevel 'slots'" % slot_ref
517
517
                self._add_result(t, n, s)
518
518
 
519
519
    def check_apparmor_profile_name_length(self):