/
mmc-password-helper.in
executable file
·208 lines (179 loc) · 6.45 KB
/
mmc-password-helper.in
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
#!/usr/bin/env python
# -*- coding: utf-8; -*-
#
# (c) 2004-2007 Linbox / Free&ALter Soft, http://linbox.com
# (c) 2007-2009 Mandriva, http://www.mandriva.com
#
# $Id$
#
# This file is part of Mandriva Management Console (MMC).
#
# MMC is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# MMC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MMC. If not, see <http://www.gnu.org/licenses/>.
"""
mmc-password-helper is a command line tool that checks password complexity, and
generates good passwords.
"""
import base64
import random
import getopt
import sys
import re
import string
import os
import ConfigParser
CONF = "@sysconfdir@/mmc-password-helper.ini"
verbose = False
pass_length = 0
generate_pass = False
must_check_pass = False
read_from_file = False
file = None
userdn = None
special_characters = "#$%&+./:=?@{}"
def error(string, force=False):
if verbose or force:
sys.stderr.write(string +"\n")
def log(string):
if verbose:
sys.stdout.write(string +"\n")
def generate_new_pass():
global pass_length
if pass_length == 0:
pass_length = 8
while True:
# Regenerate the password until check_pass() says it is a safe pass
pass_size = 3+pass_length
passtry = base64.encodestring(str(random.random())+str(random.random()))[3:pass_size]
try:
passtry = passtry.replace( passtry[random.randrange(len(passtry))], special_characters[random.randrange(len(special_characters))])
if check_pass(passtry):
log("Generated password: " + passtry)
return passtry
except:
pass
# Check if this password is safe
def check_pass(password):
try:
password = password.decode('utf-8')
except UnicodeEncodeError:
log('The password must be an UTF-8 string')
return False
if len(password) < pass_length:
log("The password length must be %s or longer" % pass_length)
return False
config = ConfigParser.RawConfigParser()
if not os.path.exists(CONF):
error("The configuration file %s doesn't exists" % CONF, True)
return False
config.read(CONF)
reg_num = re.compile(".*[0-9].*")
reg_up = re.compile(".*[A-Z].*")
reg_down = re.compile(".*[a-z].*")
if config.getboolean('main', 'must_one_number'):
if not reg_num.match(password):
log("The password must contain at least one number")
return False
if config.getboolean('main', 'must_one_upper_case'):
if not reg_up.match(password):
log("The password must contain at least one upper case character")
return False
if config.getboolean('main', 'must_one_lower_case'):
if not reg_down.match(password):
log("The password must contain at least one lower case character")
return False
if config.getboolean('main', 'must_one_special'):
found_punct = False
for punct in string.punctuation:
if password.count(punct):
found_punct = True
if not found_punct:
log("The password must contain at least one special character: #$%&+./:=?@{}")
return False
if config.getint('main', 'same_char_max') > 0:
for char in password:
if password.count(char) > config.getint('main', 'same_char_max'):
log("The password must not contain the same character %s times" % config.getint('main', 'same_char_max'))
return False
if config.getboolean('main', 'use_cracklib'):
try:
import cracklib
except ImportError:
# if we do not find python-cracklib, bypass the test
error("python-cracklib not found. Install it for a better password check")
return True
try:
if password.encode('utf-8') == cracklib.VeryFascistCheck(password.encode('utf-8')):
return True
else:
log("Password not accepted by cracklib")
except ValueError, reason:
if verbose:
log("Password not accepted: %s" % reason)
return False
return True
def usage():
print "mmc-password-helper [--help -h|--verbose -v|--newpass -n|--check -c|--length -l|--file -f]"
if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "hvncl:f:u:", ["help", "verbose", "newpass", "check", "length", "file", "user"])
except getopt.GetoptError, err:
print str(err)
sys.exit(1)
for o, a in opts:
if o in ("-v","--verbose"):
verbose = True
elif o in ("-h", "--help"):
usage()
sys.exit(1)
elif o in ("-l","--length"):
if not a.isdigit():
error("The length must be integer", True)
sys.exit(1)
pass_length = int(a)
if pass_length < 6:
error("The password can't be shorter than 6", True)
sys.exit(1)
if pass_length > 14:
error("The password can't be longer than 14", True)
sys.exit(1)
elif o in ("-n", "--newpass"):
generate_pass = True
elif o in ("-c", "--check"):
must_check_pass = True
elif o in ("-u", "--user"):
userdn = a
elif o in ("-f", "--file"):
read_from_file = True
if not os.path.isfile(a):
error("The provided file is invalid", True)
sys.exit(1)
file = a
else:
assert False, "unhandled option"
if must_check_pass and generate_pass:
error("You cannot generate a password and check it at the same time", True)
sys.exit(1)
if must_check_pass:
if read_from_file:
password = open(file).readline().strip()
else:
password = sys.stdin.readlines()[0].strip()
if check_pass(password):
sys.exit(0)
else:
sys.exit(1)
if generate_pass:
print generate_new_pass()
sys.exit(0)
usage()