~timekpr-maintainers/timekpr/trunk

« back to all changes in this revision

Viewing changes to timekpr/pam.py

  • Committer: Savvas Radevic
  • Date: 2009-11-09 04:55:50 UTC
  • Revision ID: vicedar@gmail.com-20091109045550-stw96q38g5u1s1om
* Integrated pyparsing in timekpr/pam.py
* Created a class pamparsers()
* Removed testing/timekprpam-parsing.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
370
370
            return [bfrom, bto]
371
371
    return [bf, bt]
372
372
 
 
373
 
 
374
"""
 
375
 
 
376
THESE ARE THE NEW STUFF!!!
 
377
 
 
378
"""
 
379
 
 
380
 
 
381
""" timekprpam 
 
382
    It's a Linux-PAM parser optimized for timekpr and time/access pam modules. 
 
383
    In other words, many of the linux-pam capabilities are not supported 
 
384
    (and probably will never be!).
 
385
 
 
386
    It can currently parse lines that have a comment "# Added by timekpr" at the
 
387
    end of the line.
 
388
 
 
389
    pyparsing was chosen because it's easier to look at, fix and manipulate
 
390
    (compared to simple regular expressions). However, regular expressions are
 
391
    still used in this module for simpler tasks.
 
392
""" 
 
393
 
 
394
# More information on pyparsing:
 
395
# - http://www.rexx.com/~dkuhlman/python_201/python_201.html#SECTION007600000000000000000
 
396
# - http://pyparsing.wikispaces.com/message/view/home/7002417
 
397
 
 
398
from pyparsing import *
 
399
import re
 
400
import sys
 
401
 
 
402
# CLASS: parsers(type="time.conf", input="file", file="/etc/security/time.conf")
 
403
#   type => time.conf or access.conf
 
404
#   input => file (default) or string (for testing)
 
405
#   file => filename (can be blank, will use default filenames)
 
406
# Examples? See "TEST"!
 
407
 
 
408
class pamparsers():
 
409
    def __init__(self, type, input="file", file="", string=""):
 
410
        # Set default file location if file is not defined
 
411
        self.type = type
 
412
        self.input = input
 
413
        self.file = file
 
414
        self.string = string
 
415
 
 
416
        self.result = []
 
417
        self.unrecognized = []
 
418
 
 
419
        if type == "time.conf" and input == "file" and not file:
 
420
            self.file == "/etc/security/time.conf"
 
421
        elif type == "access.conf" and input == "file" and not file:
 
422
            self.file == "/etc/security/access.conf"
 
423
 
 
424
        if input == "string" and not string:
 
425
            sys.stderr.write("ERROR: parsers() class: input is 'string' but text string is empty\n")
 
426
            sys.exit(1)
 
427
 
 
428
        self.active = self.confGetActiveLines()
 
429
        self.confParseLines()
 
430
        #self.confOutputLines()
 
431
 
 
432
    # Common
 
433
    # ======
 
434
    def getInput(self):
 
435
        """ Get input, either the file contents or the string """
 
436
        if self.input == "file":
 
437
            f = open(self.file)
 
438
            text = f.read()
 
439
            f.close()
 
440
            return self.text
 
441
        elif self.input == "string":
 
442
            return self.string
 
443
    
 
444
    def confGetActiveLines(self):
 
445
        # Ignore commented lines (they start with #)
 
446
        text = self.getInput()
 
447
        result = re.compile('^(?!\s*#).+', re.M).findall(text)
 
448
        return result
 
449
 
 
450
    def confPreCheckLine(self, line):
 
451
        """ Pre-checks the line
 
452
            Returns:
 
453
                0 = active line, with "# Added by timekpr"
 
454
                1 = active line, without "# Added by timekpr"
 
455
                2 = ignore this line
 
456
        """
 
457
        # Ignore whitespace-only line
 
458
        if re.match("^\s*$", line):
 
459
            return 2
 
460
        # timekpr lines have "# Added by timekpr" in the end
 
461
        if line[-18::] != "# Added by timekpr":
 
462
            return 0
 
463
        return 1
 
464
 
 
465
    def confParseLines(self):
 
466
        """ Outputs lines and checks for unrecognized ones (not controlled by timekpr) """
 
467
        #self.result => [original line from file, resulting parsed list]
 
468
        #self.unrecognized => unrecognized lines
 
469
 
 
470
        for line in self.active:
 
471
            test = self.confPreCheckLine(line)
 
472
 
 
473
            if test == 1:
 
474
                if self.type == "time.conf":
 
475
                    tconf_parse = self.time_conf_parser()
 
476
                    result = tconf_parse.parseString(line)
 
477
                elif self.type == "access.conf":
 
478
                    aconf_parse = self.access_conf_parser()
 
479
                    result = aconf_parse.parseString(line)
 
480
                self.result.append([line, result])
 
481
 
 
482
            elif test == 0:
 
483
                self.unrecognized.append(line)
 
484
 
 
485
            #elif test == 2: just ignore it
 
486
 
 
487
    def confOutputLines(self):
 
488
        """ Print lines and unrecognized active lines (testing purposes) """
 
489
        #if not self.result and not self.unrecognized:
 
490
        #    self.confParseLines()
 
491
        for line in self.result:
 
492
            print("%s => %s" % (line[0], line[1]))
 
493
 
 
494
        if self.unrecognized:
 
495
            list_string = "\n".join(self.unrecognized)
 
496
            print("\nWARNING: Unrecognized active lines found:\n%s" % (list_string))
 
497
 
 
498
    def confActiveLines(self):
 
499
        """ Return the self.result list """
 
500
        #self.confUnrecognizedLines()
 
501
        a = self.result
 
502
        print(a)
 
503
        return a
 
504
 
 
505
    def confUnrecognizedLines(self):
 
506
        """ Return the self.unrecognized list """
 
507
        if self.unrecognized:
 
508
            list_string = "\n".join(self.unrecognized)
 
509
            print("WARNING: Unrecognized active lines found:\n%s" % (list_string))
 
510
 
 
511
 
 
512
    # time.conf
 
513
    # =========
 
514
    # Define grammar
 
515
    # services;ttys;users;times
 
516
    # ! = NOT, & = AND, | = OR
 
517
    # * = ANY (can be used only once)
 
518
 
 
519
    # Defs
 
520
    def tconf_negation_replace(self, s, l, t):
 
521
        if t[0] == "!":
 
522
            t[0] = "block"
 
523
            return t
 
524
 
 
525
    def time_conf_parser(self):
 
526
        # Common
 
527
        tconf_commonops = "&|" # AND/OR
 
528
        # Ignore the first two ";"-separated items (services;ttys;users)
 
529
        tconf_start = Suppress(Regex("(?:.*?;){2}"))
 
530
 
 
531
        tconf_users = Regex("[^;]*")
 
532
        tconf_users.setParseAction(self.strip_whitespace)
 
533
 
 
534
        tconf_splitchar = Suppress(Word(";"))
 
535
 
 
536
        # Negation
 
537
        tconf_negation = Optional("!", "allow") # block (with "!") or allow (without "!")
 
538
        tconf_negation.setParseAction(self.tconf_negation_replace)
 
539
        # Days of week
 
540
        daysofweek = Group(OneOrMore(Literal("Mo") | Literal("Tu") | Literal("We") | Literal("Th") | Literal("Fr") | Literal("Sa") | Literal("Su") | Literal("Wk") | Literal("Wd") | Literal("Al")))
 
541
        # Get the timeofday (4 numbers and "-" and 4 numbers)
 
542
        timeofday = Group(Word(nums,exact=4) + Suppress("-") + Word(nums,exact=4))
 
543
        # Check negation, the days of week and the time of day
 
544
        tconf_time = tconf_negation + daysofweek + timeofday
 
545
        # While checking for & or | too
 
546
        tconf_time_list = Group(tconf_time) + Optional(Word(tconf_commonops))
 
547
        # Do the above all over again once or more times
 
548
        tconf_parse = tconf_start + tconf_users + tconf_splitchar + OneOrMore(tconf_time_list) + Suppress(Regex("# Added by timekpr")) + LineEnd()
 
549
        
 
550
        return tconf_parse
 
551
 
 
552
    # access.conf
 
553
    # ===========
 
554
    # Define grammar
 
555
    # permission (+ or -) : users : origins
 
556
 
 
557
    def aconf_action_replace(self, s, l, t):
 
558
        if t[0] == "-":
 
559
            t[0] = "block"
 
560
            return t
 
561
        elif t[0] == "+":
 
562
            t[0] = "allow"
 
563
            return t
 
564
 
 
565
    def strip_whitespace(self, s, l, t):
 
566
        return t[0].strip()
 
567
 
 
568
    def access_conf_parser(self):
 
569
        aconf_splitchar = Suppress(Word(":"))
 
570
        # permission - either + or -, 1 character only
 
571
        aconf_permission = Word("-+", exact=1)
 
572
        aconf_permission.setParseAction(self.aconf_action_replace)
 
573
        # users - alphanumeric and one of "_*() " characters
 
574
        aconf_users = Word(alphanums + "_*() ")
 
575
        aconf_users.setParseAction(self.strip_whitespace)
 
576
        # origins - everything else excluding "# Added by timekpr"
 
577
        aconf_origins = Regex("[^#]+")
 
578
        aconf_origins.setParseAction(self.strip_whitespace)
 
579
        aconf_parse = aconf_permission + aconf_splitchar + aconf_users + aconf_splitchar + aconf_origins
 
580
        
 
581
        return aconf_parse
 
582
 
 
583
# TEST
 
584
if __name__ == "__main__":
 
585
    # CLASS: pamparsers()
 
586
    
 
587
    # time.conf
 
588
    print("INFO: Checking time.conf\n")
 
589
 
 
590
    tconf_test_data = """
 
591
#xsh ; ttyp* ; root ; !WeMo1700-2030 | !WeFr0600-0830 # Added by timekpr
 
592
xsh & login ; ttyp* ; ro0_ters;!WdMo0000-2400 # Added by timekpr
 
593
xsh & login ; ttyp* ; root | moot;!WdMo0200-1500
 
594
xsh & login;ttyp*;root | moot;WdMo0000-2400 | Tu0800-2400 # Added by timekpr
 
595
xsh & login ; ttyp* ; root | moot;!WdMo0700-1500 & !MoWeFr1500-2000 # Added by timekpr
 
596
a;o; a; e
 
597
      
 
598
 
 
599
    """
 
600
    pamparsers(type="time.conf", input="string", string=tconf_test_data).confOutputLines()
 
601
#    list = pamparsers(type="time.conf", input="string", string=tconf_test_data).confActiveLines()
 
602
#    for x in list:
 
603
#        print(x)
 
604
 
 
605
    # access.conf
 
606
    print("\nINFO: Checking access.conf\n")
 
607
 
 
608
    aconf_test_data = """
 
609
# testing # Added by timekpr
 
610
- : lala : ALL # Added by timekpr
 
611
-:lala:ALL # Added by timekpr
 
612
- : nana123_a : ALL # Added by timekpr
 
613
- : testing : ALL # test
 
614
- : testing : ALL EXCEPT root
 
615
+ : john : 2001:4ca0:0:101::1# Added by timekpr
 
616
+ : root : .foo.bar.org  # Added by timekpr
 
617
- : john : 2001:4ca0:0:101::/64 # Added by timekpr
 
618
      
 
619
 
 
620
    """
 
621
    pamparsers(type="access.conf", input="string", string=aconf_test_data).confOutputLines()
 
622
#    list2 = pamparsers(type="access.conf", input="string", string=aconf_test_data).confActiveLines()
 
623
#    for z in list2:
 
624
#        print(z)
 
625