]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors | |
3 | * | |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | #include "squid.h" | |
10 | #include "auth/basic/Config.h" | |
11 | #include "auth/basic/User.h" | |
12 | #include "auth/basic/UserRequest.h" | |
13 | #include "auth/QueueNode.h" | |
14 | #include "auth/State.h" | |
15 | #include "Debug.h" | |
16 | #include "format/Format.h" | |
17 | #include "helper.h" | |
18 | #include "helper/Reply.h" | |
19 | #include "HttpRequest.h" | |
20 | #include "MemBuf.h" | |
21 | #include "rfc1738.h" | |
22 | #include "SquidTime.h" | |
23 | ||
24 | #if !defined(HELPER_INPUT_BUFFER) | |
25 | #define HELPER_INPUT_BUFFER 8192 | |
26 | #endif | |
27 | ||
28 | int | |
29 | Auth::Basic::UserRequest::authenticated() const | |
30 | { | |
31 | Auth::Basic::User const *basic_auth = dynamic_cast<Auth::Basic::User const *>(user().getRaw()); | |
32 | ||
33 | if (basic_auth && basic_auth->authenticated()) | |
34 | return 1; | |
35 | ||
36 | return 0; | |
37 | } | |
38 | ||
39 | const char * | |
40 | Auth::Basic::UserRequest::credentialsStr() | |
41 | { | |
42 | Auth::Basic::User const *basic_auth = dynamic_cast<Auth::Basic::User const *>(user().getRaw()); | |
43 | if (basic_auth) | |
44 | return basic_auth->passwd; | |
45 | return NULL; | |
46 | } | |
47 | ||
48 | /* log a basic user in | |
49 | */ | |
50 | void | |
51 | Auth::Basic::UserRequest::authenticate(HttpRequest *, ConnStateData *, Http::HdrType) | |
52 | { | |
53 | assert(user() != NULL); | |
54 | ||
55 | /* if the password is not ok, do an identity */ | |
56 | if (!user() || user()->credentials() != Auth::Ok) | |
57 | return; | |
58 | ||
59 | /* are we about to recheck the credentials externally? */ | |
60 | if ((user()->expiretime + static_cast<Auth::Basic::Config*>(Auth::SchemeConfig::Find("basic"))->credentialsTTL) <= squid_curtime) { | |
61 | debugs(29, 4, HERE << "credentials expired - rechecking"); | |
62 | return; | |
63 | } | |
64 | ||
65 | /* we have been through the external helper, and the credentials haven't expired */ | |
66 | debugs(29, 9, HERE << "user '" << user()->username() << "' authenticated"); | |
67 | ||
68 | /* Decode now takes care of finding the AuthUser struct in the cache */ | |
69 | /* after external auth occurs anyway */ | |
70 | user()->expiretime = current_time.tv_sec; | |
71 | } | |
72 | ||
73 | Auth::Direction | |
74 | Auth::Basic::UserRequest::module_direction() | |
75 | { | |
76 | /* null auth_user is checked for by Auth::UserRequest::direction() */ | |
77 | if (user()->auth_type != Auth::AUTH_BASIC) | |
78 | return Auth::CRED_ERROR; | |
79 | ||
80 | switch (user()->credentials()) { | |
81 | ||
82 | case Auth::Unchecked: | |
83 | case Auth::Pending: | |
84 | return Auth::CRED_LOOKUP; | |
85 | ||
86 | case Auth::Ok: | |
87 | if (user()->expiretime + static_cast<Auth::Basic::Config*>(Auth::SchemeConfig::Find("basic"))->credentialsTTL <= squid_curtime) | |
88 | return Auth::CRED_LOOKUP; | |
89 | return Auth::CRED_VALID; | |
90 | ||
91 | case Auth::Failed: | |
92 | return Auth::CRED_VALID; | |
93 | ||
94 | default: | |
95 | return Auth::CRED_ERROR; | |
96 | } | |
97 | } | |
98 | ||
99 | /* send the initial data to a basic authenticator module */ | |
100 | void | |
101 | Auth::Basic::UserRequest::startHelperLookup(HttpRequest *request, AccessLogEntry::Pointer &al, AUTHCB * handler, void *data) | |
102 | { | |
103 | assert(user()->auth_type == Auth::AUTH_BASIC); | |
104 | Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(user().getRaw()); | |
105 | assert(basic_auth != NULL); | |
106 | debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'"); | |
107 | ||
108 | if (static_cast<Auth::Basic::Config*>(Auth::SchemeConfig::Find("basic"))->authenticateProgram == NULL) { | |
109 | debugs(29, DBG_CRITICAL, "ERROR: No Basic authentication program configured."); | |
110 | handler(data); | |
111 | return; | |
112 | } | |
113 | ||
114 | /* check to see if the auth_user already has a request outstanding */ | |
115 | if (user()->credentials() == Auth::Pending) { | |
116 | /* there is a request with the same credentials already being verified */ | |
117 | ||
118 | Auth::QueueNode *node = new Auth::QueueNode(this, handler, data); | |
119 | ||
120 | /* queue this validation request to be infored of the pending lookup results */ | |
121 | node->next = basic_auth->queue; | |
122 | basic_auth->queue = node; | |
123 | return; | |
124 | } | |
125 | // otherwise submit this request to the auth helper(s) for validation | |
126 | ||
127 | /* mark this user as having verification in progress */ | |
128 | user()->credentials(Auth::Pending); | |
129 | char buf[HELPER_INPUT_BUFFER]; | |
130 | static char usern[HELPER_INPUT_BUFFER]; | |
131 | static char pass[HELPER_INPUT_BUFFER]; | |
132 | ||
133 | xstrncpy(usern, rfc1738_escape(user()->username()), sizeof(usern)); | |
134 | xstrncpy(pass, rfc1738_escape(basic_auth->passwd), sizeof(pass)); | |
135 | ||
136 | int sz = 0; | |
137 | if (const char *keyExtras = helperRequestKeyExtras(request, al)) | |
138 | sz = snprintf(buf, sizeof(buf), "%s %s %s\n", usern, pass, keyExtras); | |
139 | else | |
140 | sz = snprintf(buf, sizeof(buf), "%s %s\n", usern, pass); | |
141 | ||
142 | if (sz<=0) { | |
143 | debugs(9, DBG_CRITICAL, "ERROR: Basic Authentication Failure. Can not build helper validation request."); | |
144 | handler(data); | |
145 | } else if (static_cast<size_t>(sz) >= sizeof(buf)) { | |
146 | debugs(9, DBG_CRITICAL, "ERROR: Basic Authentication Failure. user:password exceeds " << sizeof(buf) << " bytes."); | |
147 | handler(data); | |
148 | } else | |
149 | helperSubmit(basicauthenticators, buf, Auth::Basic::UserRequest::HandleReply, | |
150 | new Auth::StateData(this, handler, data)); | |
151 | } | |
152 | ||
153 | void | |
154 | Auth::Basic::UserRequest::HandleReply(void *data, const Helper::Reply &reply) | |
155 | { | |
156 | Auth::StateData *r = static_cast<Auth::StateData *>(data); | |
157 | void *cbdata; | |
158 | debugs(29, 5, HERE << "reply=" << reply); | |
159 | ||
160 | assert(r->auth_user_request != NULL); | |
161 | assert(r->auth_user_request->user()->auth_type == Auth::AUTH_BASIC); | |
162 | ||
163 | // add new helper kv-pair notes to the credentials object | |
164 | // so that any transaction using those credentials can access them | |
165 | static const NotePairs::Names appendables = { SBuf("group"), SBuf("tag") }; | |
166 | r->auth_user_request->user()->notes.replaceOrAddOrAppend(&reply.notes, appendables); | |
167 | ||
168 | /* this is okay since we only play with the Auth::Basic::User child fields below | |
169 | * and do not pass the pointer itself anywhere */ | |
170 | Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(r->auth_user_request->user().getRaw()); | |
171 | ||
172 | assert(basic_auth != NULL); | |
173 | ||
174 | if (reply.result == Helper::Okay) | |
175 | basic_auth->credentials(Auth::Ok); | |
176 | else { | |
177 | basic_auth->credentials(Auth::Failed); | |
178 | ||
179 | if (reply.other().hasContent()) | |
180 | r->auth_user_request->setDenyMessage(reply.other().content()); | |
181 | } | |
182 | ||
183 | basic_auth->expiretime = squid_curtime; | |
184 | ||
185 | if (cbdataReferenceValidDone(r->data, &cbdata)) | |
186 | r->handler(cbdata); | |
187 | ||
188 | cbdataReferenceDone(r->data); | |
189 | ||
190 | while (basic_auth->queue) { | |
191 | if (cbdataReferenceValidDone(basic_auth->queue->data, &cbdata)) | |
192 | basic_auth->queue->handler(cbdata); | |
193 | ||
194 | Auth::QueueNode *tmpnode = basic_auth->queue->next; | |
195 | basic_auth->queue->next = NULL; | |
196 | delete basic_auth->queue; | |
197 | ||
198 | basic_auth->queue = tmpnode; | |
199 | } | |
200 | ||
201 | delete r; | |
202 | } | |
203 |