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 Authentication functions to answer to Basic and Digest HTTP authentication
44 challenges
45 '''
46
47 from base64 import b64encode
48
49 from httxutil import *
50
51 -def authbasic(username, password, authchallenge):
52 '''
53 Given a username and a password return an HTTP Basic Authentication
54 challenge.
55
56 The authchallenge parameter is just for harmony with authdigest
57 and for potentially defining a plug-in interface for other
58 authentications
59
60 @param username: The username to reply to the challenge
61 @type username: str
62 @param password: The password to reply to the challenge
63 @type password: str
64 @return: A tuple containing a base64 encoded challenge and
65 a string with a cache-able challenge for future responses
66 The second value can be None
67 @rtype: tuple (str, str|None)
68 '''
69
70
71 base64auth = b64encode('%s:%s' % (username, password)).rstrip()
72
73 return ('basic', base64auth, None)
74
75
76 -def authdigest(username, password, challenge, request, nonce_count):
77 '''
78 Given a username, password, a list of challenge fields, a request
79 a sequence number (nonce_count) return a Diget Auth challenge
80 answer
81
82 If username is None, then challenge will contain a username key with
83 the username and the password parameter will be the cached value of
84 the HA1 (see the function below) to be used
85
86 @param username: The username to reply to the challenge or None to
87 indicate that the password contains a HA1 cache
88 @type username: str|None
89 @param password: The password to reply to the challenge or a HA1 cache
90 @type password: str
91 @param challenge: Dictionary with the parameters in the challenge
92 @type challenge: dict
93 @param request: The request that has generated the auth challenge
94 @type request: L{HttxRequest}
95 @param nonce_count: Sequence value if this not the first time such
96 a request has been seen. Used by Digest auth to
97 prevent replay attacks
98 @type nonce_count: int
99 @return: A tuple containing a digest answer and
100 a string with a cache-able challenge for future responses
101 Both values can be None if a specific non-supported variant
102 of the Digest Auth is used
103 @rtype: tuple (str|None, str|None)
104 '''
105 try:
106 realm = challenge['realm']
107 nonce = challenge['nonce']
108 except KeyError:
109 return (None, None, None)
110
111 qop = challenge.get('qop')
112 algorithm = challenge.get('algorithm', 'MD5')
113
114 opaque = challenge.get('opaque', None)
115
116 H, KD = get_algorithm_impls(algorithm)
117 if H is None:
118 return (None, None, None)
119
120 entdig = None
121
122 if not username:
123 username = challenge['username']
124 HA1 = password
125 else:
126 A1 = "%s:%s:%s" % (username, realm, password)
127 HA1 = H(A1)
128
129
130 A2 = "%s:%s" % (request.get_method(), request.get_selector())
131 if qop == 'auth':
132
133
134 cnonce = get_cnonce(nonce_count, nonce)
135
136 ncvalue = '%08x' % nonce_count
137 noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
138
139 respdig = KD(HA1, noncebit)
140 elif qop is None:
141 respdig = KD(HA1, "%s:%s" % (nonce, H(A2)))
142 else:
143
144
145 return (None, None, None)
146
147
148
149 base = 'username="%s", realm="%s", nonce="%s", uri="%s", response="%s"' \
150 % (username, realm, nonce, request.get_selector(), respdig)
151 if opaque:
152 base += ', opaque="%s"' % opaque
153 if entdig:
154 base += ', digest="%s"' % entdig
155 base += ', algorithm="%s"' % algorithm
156 if qop:
157 base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
158
159 return ('digest', base, HA1)
160