root / vncauthproxy / passwd.py @ f1facf89
History | View | Annotate | Download (4.3 kB)
1 | 3b98303f | Stratos Psomadakis | #!/usr/bin/env python
|
---|---|---|---|
2 | 3b98303f | Stratos Psomadakis | """
|
3 | 3b98303f | Stratos Psomadakis | vncauthproxy-passwd - vncauthproxy passwd file mgmt tool
|
4 | 3b98303f | Stratos Psomadakis | """
|
5 | 3b98303f | Stratos Psomadakis | #
|
6 | 3b98303f | Stratos Psomadakis | # Copyright (c) 2010-2013 Greek Research and Technology Network S.A.
|
7 | 3b98303f | Stratos Psomadakis | #
|
8 | 3b98303f | Stratos Psomadakis | # This program is free software; you can redistribute it and/or modify
|
9 | 3b98303f | Stratos Psomadakis | # it under the terms of the GNU General Public License as published by
|
10 | 3b98303f | Stratos Psomadakis | # the Free Software Foundation; either version 2 of the License, or
|
11 | 3b98303f | Stratos Psomadakis | # (at your option) any later version.
|
12 | 3b98303f | Stratos Psomadakis | #
|
13 | 3b98303f | Stratos Psomadakis | # This program is distributed in the hope that it will be useful, but
|
14 | 3b98303f | Stratos Psomadakis | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
15 | 3b98303f | Stratos Psomadakis | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
16 | 3b98303f | Stratos Psomadakis | # General Public License for more details.
|
17 | 3b98303f | Stratos Psomadakis | #
|
18 | 3b98303f | Stratos Psomadakis | # You should have received a copy of the GNU General Public License
|
19 | 3b98303f | Stratos Psomadakis | # along with this program; if not, write to the Free Software
|
20 | 3b98303f | Stratos Psomadakis | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
21 | 3b98303f | Stratos Psomadakis | # 02110-1301, USA.
|
22 | 3b98303f | Stratos Psomadakis | |
23 | 3b98303f | Stratos Psomadakis | import argparse |
24 | 3b98303f | Stratos Psomadakis | import crypt |
25 | 3b98303f | Stratos Psomadakis | import getpass |
26 | 3b98303f | Stratos Psomadakis | import os |
27 | 3b98303f | Stratos Psomadakis | import random |
28 | 3b98303f | Stratos Psomadakis | import re |
29 | 3b98303f | Stratos Psomadakis | import string |
30 | 3b98303f | Stratos Psomadakis | import sys |
31 | 3b98303f | Stratos Psomadakis | import tempfile |
32 | 3b98303f | Stratos Psomadakis | |
33 | 3b98303f | Stratos Psomadakis | |
34 | 3b98303f | Stratos Psomadakis | def parse_arguments(): |
35 | 3b98303f | Stratos Psomadakis | """ Parse cli args. """
|
36 | 3b98303f | Stratos Psomadakis | parser = argparse.ArgumentParser() |
37 | 3b98303f | Stratos Psomadakis | |
38 | 3b98303f | Stratos Psomadakis | parser.add_argument("-n", "--dry-run", action="store_true", dest="dry_run", |
39 | 3b98303f | Stratos Psomadakis | help="Display the results on stdout without updating "
|
40 | 3b98303f | Stratos Psomadakis | "the passwd file")
|
41 | 3b98303f | Stratos Psomadakis | parser.add_argument("-D", "--delete", action="store_true", |
42 | 3b98303f | Stratos Psomadakis | dest="delete_user", help="Delete user from file") |
43 | 3b98303f | Stratos Psomadakis | parser.add_argument("passwdfile", metavar="file", type=str, nargs=1, |
44 | 3b98303f | Stratos Psomadakis | help="Path to the passwd file")
|
45 | 3b98303f | Stratos Psomadakis | parser.add_argument("user", metavar="user", type=str, nargs=1, |
46 | 3b98303f | Stratos Psomadakis | help="User to edit")
|
47 | 3b98303f | Stratos Psomadakis | |
48 | 3b98303f | Stratos Psomadakis | args = parser.parse_args() |
49 | 3b98303f | Stratos Psomadakis | |
50 | 3b98303f | Stratos Psomadakis | return args
|
51 | 3b98303f | Stratos Psomadakis | |
52 | 3b98303f | Stratos Psomadakis | |
53 | 3b98303f | Stratos Psomadakis | def gen_salt(): |
54 | 3b98303f | Stratos Psomadakis | """ Generate 16-char salt string. """
|
55 | 3b98303f | Stratos Psomadakis | chars = list(string.ascii_letters + string.digits + "./") |
56 | 3b98303f | Stratos Psomadakis | random.shuffle(chars) |
57 | 3b98303f | Stratos Psomadakis | |
58 | 3b98303f | Stratos Psomadakis | return "".join(random.choice(chars) for x in range(16)) |
59 | 3b98303f | Stratos Psomadakis | |
60 | 3b98303f | Stratos Psomadakis | |
61 | 3b98303f | Stratos Psomadakis | def gen_hash(username, password): |
62 | 3b98303f | Stratos Psomadakis | """ Generate SHA-512 hash in crypt() format. """
|
63 | 3b98303f | Stratos Psomadakis | salt = "$6$%s$" % gen_salt()
|
64 | 3b98303f | Stratos Psomadakis | return "%s:%s\n" % (username, crypt.crypt(password, salt)) |
65 | 3b98303f | Stratos Psomadakis | |
66 | 3b98303f | Stratos Psomadakis | |
67 | 3b98303f | Stratos Psomadakis | def fail(reason): |
68 | 3b98303f | Stratos Psomadakis | """ Print reason for failure and exit. """
|
69 | 3b98303f | Stratos Psomadakis | sys.stderr.write("%s\n" % reason)
|
70 | 3b98303f | Stratos Psomadakis | sys.exit(1)
|
71 | 3b98303f | Stratos Psomadakis | |
72 | 3b98303f | Stratos Psomadakis | |
73 | 3b98303f | Stratos Psomadakis | def find_user(lines, user): |
74 | 3b98303f | Stratos Psomadakis | """ Return user info from passwd file, if the users exists. """
|
75 | 3b98303f | Stratos Psomadakis | for (idx, line) in enumerate(lines): |
76 | 3b98303f | Stratos Psomadakis | (username, _) = line.split(":", 1) |
77 | 3b98303f | Stratos Psomadakis | if user == username:
|
78 | 3b98303f | Stratos Psomadakis | return (idx, line)
|
79 | 3b98303f | Stratos Psomadakis | |
80 | 3b98303f | Stratos Psomadakis | return None |
81 | 3b98303f | Stratos Psomadakis | |
82 | 3b98303f | Stratos Psomadakis | |
83 | 3b98303f | Stratos Psomadakis | def write_wrapper(passwdfile, lines, dry_run): |
84 | 3b98303f | Stratos Psomadakis | """ Dry-run wrapper for write. """
|
85 | 3b98303f | Stratos Psomadakis | if not dry_run: |
86 | c7da5329 | Stratos Psomadakis | (fd, fpath) = tempfile.mkstemp(dir=os.path.dirname(passwdfile)) |
87 | 3b98303f | Stratos Psomadakis | with os.fdopen(fd, "w+") as f: |
88 | 3b98303f | Stratos Psomadakis | f.write("".join(lines))
|
89 | c7da5329 | Stratos Psomadakis | os.rename(fpath, passwdfile) |
90 | 3b98303f | Stratos Psomadakis | else:
|
91 | f1facf89 | Stratos Psomadakis | sys.stdout.write("".join(lines))
|
92 | 3b98303f | Stratos Psomadakis | |
93 | 3b98303f | Stratos Psomadakis | |
94 | 3b98303f | Stratos Psomadakis | def delete_user(user, passwdfile): |
95 | 3b98303f | Stratos Psomadakis | """ Delete user from passwdfile. """
|
96 | 3b98303f | Stratos Psomadakis | if not os.path.isfile(passwdfile): |
97 | 3b98303f | Stratos Psomadakis | fail("Cannot delete user from non-existent file")
|
98 | 3b98303f | Stratos Psomadakis | |
99 | 3b98303f | Stratos Psomadakis | lines = open(passwdfile).readlines()
|
100 | 3b98303f | Stratos Psomadakis | user_line = find_user(lines, user) |
101 | 3b98303f | Stratos Psomadakis | if not user_line: |
102 | 3b98303f | Stratos Psomadakis | fail("User not found!")
|
103 | 3b98303f | Stratos Psomadakis | |
104 | 3b98303f | Stratos Psomadakis | (idx, line) = user_line |
105 | 3b98303f | Stratos Psomadakis | lines.remove(line) |
106 | 3b98303f | Stratos Psomadakis | return lines
|
107 | 3b98303f | Stratos Psomadakis | |
108 | 3b98303f | Stratos Psomadakis | |
109 | 3b98303f | Stratos Psomadakis | def add_or_update_user(user, passwdfile): |
110 | 3b98303f | Stratos Psomadakis | """ Add or update user from passwdfile. """
|
111 | 3b98303f | Stratos Psomadakis | password = getpass.getpass() |
112 | 3b98303f | Stratos Psomadakis | if password == "": |
113 | 3b98303f | Stratos Psomadakis | fail("Password cannot be empty")
|
114 | 3b98303f | Stratos Psomadakis | |
115 | 3b98303f | Stratos Psomadakis | if password != getpass.getpass("Retype password: "): |
116 | 3b98303f | Stratos Psomadakis | fail("Passwords don't match")
|
117 | 3b98303f | Stratos Psomadakis | |
118 | 3b98303f | Stratos Psomadakis | newline = gen_hash(user, password) |
119 | 3b98303f | Stratos Psomadakis | |
120 | 3b98303f | Stratos Psomadakis | lines = [newline] |
121 | 3b98303f | Stratos Psomadakis | if os.path.isfile(passwdfile):
|
122 | 3b98303f | Stratos Psomadakis | lines = open(passwdfile).readlines()
|
123 | 3b98303f | Stratos Psomadakis | user_line = find_user(lines, user) |
124 | 3b98303f | Stratos Psomadakis | if not user_line: |
125 | 3b98303f | Stratos Psomadakis | lines.append(newline) |
126 | 3b98303f | Stratos Psomadakis | else:
|
127 | 3b98303f | Stratos Psomadakis | (idx, _) = user_line |
128 | 3b98303f | Stratos Psomadakis | lines[idx] = newline |
129 | 3b98303f | Stratos Psomadakis | |
130 | 3b98303f | Stratos Psomadakis | return lines
|
131 | 3b98303f | Stratos Psomadakis | |
132 | 3b98303f | Stratos Psomadakis | |
133 | 3b98303f | Stratos Psomadakis | def main(): |
134 | 3b98303f | Stratos Psomadakis | """ Run the tool from the command line. """
|
135 | 3b98303f | Stratos Psomadakis | try:
|
136 | 3b98303f | Stratos Psomadakis | args = parse_arguments() |
137 | 3b98303f | Stratos Psomadakis | |
138 | 3b98303f | Stratos Psomadakis | user = args.user[0]
|
139 | 3b98303f | Stratos Psomadakis | passwdfile = args.passwdfile[0]
|
140 | 3b98303f | Stratos Psomadakis | |
141 | 3b98303f | Stratos Psomadakis | user_re = r'^[a-z_][a-z0-9_]{0,30}$'
|
142 | 3b98303f | Stratos Psomadakis | if re.match(user_re, user) is None: |
143 | 3b98303f | Stratos Psomadakis | fail("Username must match the following regexp: %s" % user_re)
|
144 | 3b98303f | Stratos Psomadakis | |
145 | 3b98303f | Stratos Psomadakis | if args.delete_user:
|
146 | 3b98303f | Stratos Psomadakis | lines = delete_user(user, passwdfile) |
147 | 3b98303f | Stratos Psomadakis | else:
|
148 | 3b98303f | Stratos Psomadakis | lines = add_or_update_user(user, passwdfile) |
149 | 3b98303f | Stratos Psomadakis | |
150 | 3b98303f | Stratos Psomadakis | write_wrapper(passwdfile, lines, args.dry_run) |
151 | 3b98303f | Stratos Psomadakis | except KeyboardInterrupt: |
152 | 3b98303f | Stratos Psomadakis | pass
|
153 | 3b98303f | Stratos Psomadakis | |
154 | 3b98303f | Stratos Psomadakis | sys.exit(0) |