OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // See "SSPI Sample Application" at | 5 // See "SSPI Sample Application" at |
6 // http://msdn.microsoft.com/en-us/library/aa918273.aspx | 6 // http://msdn.microsoft.com/en-us/library/aa918273.aspx |
7 | 7 |
8 #include "net/http/http_auth_sspi_win.h" | 8 #include "net/http/http_auth_sspi_win.h" |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 int MapAcquireCredentialsStatusToError(SECURITY_STATUS status, | 22 int MapAcquireCredentialsStatusToError(SECURITY_STATUS status, |
23 const SEC_WCHAR* package) { | 23 const SEC_WCHAR* package) { |
24 VLOG(1) << "AcquireCredentialsHandle returned 0x" << std::hex << status; | 24 VLOG(1) << "AcquireCredentialsHandle returned 0x" << std::hex << status; |
25 switch (status) { | 25 switch (status) { |
26 case SEC_E_OK: | 26 case SEC_E_OK: |
27 return OK; | 27 return OK; |
28 case SEC_E_INSUFFICIENT_MEMORY: | 28 case SEC_E_INSUFFICIENT_MEMORY: |
29 return ERR_OUT_OF_MEMORY; | 29 return ERR_OUT_OF_MEMORY; |
30 case SEC_E_INTERNAL_ERROR: | 30 case SEC_E_INTERNAL_ERROR: |
31 LOG(WARNING) | 31 LOG(WARNING) << "AcquireCredentialsHandle returned unexpected status 0x" |
32 << "AcquireCredentialsHandle returned unexpected status 0x" | 32 << std::hex << status; |
33 << std::hex << status; | |
34 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; | 33 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
35 case SEC_E_NO_CREDENTIALS: | 34 case SEC_E_NO_CREDENTIALS: |
36 case SEC_E_NOT_OWNER: | 35 case SEC_E_NOT_OWNER: |
37 case SEC_E_UNKNOWN_CREDENTIALS: | 36 case SEC_E_UNKNOWN_CREDENTIALS: |
38 return ERR_INVALID_AUTH_CREDENTIALS; | 37 return ERR_INVALID_AUTH_CREDENTIALS; |
39 case SEC_E_SECPKG_NOT_FOUND: | 38 case SEC_E_SECPKG_NOT_FOUND: |
40 // This indicates that the SSPI configuration does not match expectations | 39 // This indicates that the SSPI configuration does not match expectations |
41 return ERR_UNSUPPORTED_AUTH_SCHEME; | 40 return ERR_UNSUPPORTED_AUTH_SCHEME; |
42 default: | 41 default: |
43 LOG(WARNING) | 42 LOG(WARNING) << "AcquireCredentialsHandle returned undocumented status 0x" |
44 << "AcquireCredentialsHandle returned undocumented status 0x" | 43 << std::hex << status; |
45 << std::hex << status; | |
46 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; | 44 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
47 } | 45 } |
48 } | 46 } |
49 | 47 |
50 int AcquireExplicitCredentials(SSPILibrary* library, | 48 int AcquireExplicitCredentials(SSPILibrary* library, |
51 const SEC_WCHAR* package, | 49 const SEC_WCHAR* package, |
52 const base::string16& domain, | 50 const base::string16& domain, |
53 const base::string16& user, | 51 const base::string16& user, |
54 const base::string16& password, | 52 const base::string16& password, |
55 CredHandle* cred) { | 53 CredHandle* cred) { |
56 SEC_WINNT_AUTH_IDENTITY identity; | 54 SEC_WINNT_AUTH_IDENTITY identity; |
57 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; | 55 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
58 identity.User = | 56 identity.User = |
59 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str())); | 57 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str())); |
60 identity.UserLength = user.size(); | 58 identity.UserLength = user.size(); |
61 identity.Domain = | 59 identity.Domain = |
62 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str())); | 60 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str())); |
63 identity.DomainLength = domain.size(); | 61 identity.DomainLength = domain.size(); |
64 identity.Password = | 62 identity.Password = |
65 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str())); | 63 reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str())); |
66 identity.PasswordLength = password.size(); | 64 identity.PasswordLength = password.size(); |
67 | 65 |
68 TimeStamp expiry; | 66 TimeStamp expiry; |
69 | 67 |
70 // Pass the username/password to get the credentials handle. | 68 // Pass the username/password to get the credentials handle. |
71 SECURITY_STATUS status = library->AcquireCredentialsHandle( | 69 SECURITY_STATUS status = library->AcquireCredentialsHandle( |
72 NULL, // pszPrincipal | 70 NULL, // pszPrincipal |
73 const_cast<SEC_WCHAR*>(package), // pszPackage | 71 const_cast<SEC_WCHAR*>(package), // pszPackage |
74 SECPKG_CRED_OUTBOUND, // fCredentialUse | 72 SECPKG_CRED_OUTBOUND, // fCredentialUse |
75 NULL, // pvLogonID | 73 NULL, // pvLogonID |
76 &identity, // pAuthData | 74 &identity, // pAuthData |
77 NULL, // pGetKeyFn (not used) | 75 NULL, // pGetKeyFn (not used) |
78 NULL, // pvGetKeyArgument (not used) | 76 NULL, // pvGetKeyArgument (not used) |
79 cred, // phCredential | 77 cred, // phCredential |
80 &expiry); // ptsExpiry | 78 &expiry); // ptsExpiry |
81 | 79 |
82 return MapAcquireCredentialsStatusToError(status, package); | 80 return MapAcquireCredentialsStatusToError(status, package); |
83 } | 81 } |
84 | 82 |
85 int AcquireDefaultCredentials(SSPILibrary* library, const SEC_WCHAR* package, | 83 int AcquireDefaultCredentials(SSPILibrary* library, |
| 84 const SEC_WCHAR* package, |
86 CredHandle* cred) { | 85 CredHandle* cred) { |
87 TimeStamp expiry; | 86 TimeStamp expiry; |
88 | 87 |
89 // Pass the username/password to get the credentials handle. | 88 // Pass the username/password to get the credentials handle. |
90 // Note: Since the 5th argument is NULL, it uses the default | 89 // Note: Since the 5th argument is NULL, it uses the default |
91 // cached credentials for the logged in user, which can be used | 90 // cached credentials for the logged in user, which can be used |
92 // for a single sign-on. | 91 // for a single sign-on. |
93 SECURITY_STATUS status = library->AcquireCredentialsHandle( | 92 SECURITY_STATUS status = library->AcquireCredentialsHandle( |
94 NULL, // pszPrincipal | 93 NULL, // pszPrincipal |
95 const_cast<SEC_WCHAR*>(package), // pszPackage | 94 const_cast<SEC_WCHAR*>(package), // pszPackage |
96 SECPKG_CRED_OUTBOUND, // fCredentialUse | 95 SECPKG_CRED_OUTBOUND, // fCredentialUse |
97 NULL, // pvLogonID | 96 NULL, // pvLogonID |
98 NULL, // pAuthData | 97 NULL, // pAuthData |
99 NULL, // pGetKeyFn (not used) | 98 NULL, // pGetKeyFn (not used) |
100 NULL, // pvGetKeyArgument (not used) | 99 NULL, // pvGetKeyArgument (not used) |
101 cred, // phCredential | 100 cred, // phCredential |
102 &expiry); // ptsExpiry | 101 &expiry); // ptsExpiry |
103 | 102 |
104 return MapAcquireCredentialsStatusToError(status, package); | 103 return MapAcquireCredentialsStatusToError(status, package); |
105 } | 104 } |
106 | 105 |
107 int MapInitializeSecurityContextStatusToError(SECURITY_STATUS status) { | 106 int MapInitializeSecurityContextStatusToError(SECURITY_STATUS status) { |
108 VLOG(1) << "InitializeSecurityContext returned 0x" << std::hex << status; | 107 VLOG(1) << "InitializeSecurityContext returned 0x" << std::hex << status; |
109 switch (status) { | 108 switch (status) { |
110 case SEC_E_OK: | 109 case SEC_E_OK: |
111 case SEC_I_CONTINUE_NEEDED: | 110 case SEC_I_CONTINUE_NEEDED: |
112 return OK; | 111 return OK; |
113 case SEC_I_COMPLETE_AND_CONTINUE: | 112 case SEC_I_COMPLETE_AND_CONTINUE: |
114 case SEC_I_COMPLETE_NEEDED: | 113 case SEC_I_COMPLETE_NEEDED: |
115 case SEC_I_INCOMPLETE_CREDENTIALS: | 114 case SEC_I_INCOMPLETE_CREDENTIALS: |
116 case SEC_E_INCOMPLETE_MESSAGE: | 115 case SEC_E_INCOMPLETE_MESSAGE: |
117 case SEC_E_INTERNAL_ERROR: | 116 case SEC_E_INTERNAL_ERROR: |
118 // These are return codes reported by InitializeSecurityContext | 117 // These are return codes reported by InitializeSecurityContext |
119 // but not expected by Chrome (for example, INCOMPLETE_CREDENTIALS | 118 // but not expected by Chrome (for example, INCOMPLETE_CREDENTIALS |
120 // and INCOMPLETE_MESSAGE are intended for schannel). | 119 // and INCOMPLETE_MESSAGE are intended for schannel). |
121 LOG(WARNING) | 120 LOG(WARNING) << "InitializeSecurityContext returned unexpected status 0x" |
122 << "InitializeSecurityContext returned unexpected status 0x" | 121 << std::hex << status; |
123 << std::hex << status; | |
124 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; | 122 return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
125 case SEC_E_INSUFFICIENT_MEMORY: | 123 case SEC_E_INSUFFICIENT_MEMORY: |
126 return ERR_OUT_OF_MEMORY; | 124 return ERR_OUT_OF_MEMORY; |
127 case SEC_E_UNSUPPORTED_FUNCTION: | 125 case SEC_E_UNSUPPORTED_FUNCTION: |
128 NOTREACHED(); | 126 NOTREACHED(); |
129 return ERR_UNEXPECTED; | 127 return ERR_UNEXPECTED; |
130 case SEC_E_INVALID_HANDLE: | 128 case SEC_E_INVALID_HANDLE: |
131 NOTREACHED(); | 129 NOTREACHED(); |
132 return ERR_INVALID_HANDLE; | 130 return ERR_INVALID_HANDLE; |
133 case SEC_E_INVALID_TOKEN: | 131 case SEC_E_INVALID_TOKEN: |
(...skipping 17 matching lines...) Expand all Loading... |
151 int MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status) { | 149 int MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status) { |
152 VLOG(1) << "QuerySecurityPackageInfo returned 0x" << std::hex << status; | 150 VLOG(1) << "QuerySecurityPackageInfo returned 0x" << std::hex << status; |
153 switch (status) { | 151 switch (status) { |
154 case SEC_E_OK: | 152 case SEC_E_OK: |
155 return OK; | 153 return OK; |
156 case SEC_E_SECPKG_NOT_FOUND: | 154 case SEC_E_SECPKG_NOT_FOUND: |
157 // This isn't a documented return code, but has been encountered | 155 // This isn't a documented return code, but has been encountered |
158 // during testing. | 156 // during testing. |
159 return ERR_UNSUPPORTED_AUTH_SCHEME; | 157 return ERR_UNSUPPORTED_AUTH_SCHEME; |
160 default: | 158 default: |
161 LOG(WARNING) | 159 LOG(WARNING) << "QuerySecurityPackageInfo returned undocumented status 0x" |
162 << "QuerySecurityPackageInfo returned undocumented status 0x" | 160 << std::hex << status; |
163 << std::hex << status; | |
164 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; | 161 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
165 } | 162 } |
166 } | 163 } |
167 | 164 |
168 int MapFreeContextBufferStatusToError(SECURITY_STATUS status) { | 165 int MapFreeContextBufferStatusToError(SECURITY_STATUS status) { |
169 VLOG(1) << "FreeContextBuffer returned 0x" << std::hex << status; | 166 VLOG(1) << "FreeContextBuffer returned 0x" << std::hex << status; |
170 switch (status) { | 167 switch (status) { |
171 case SEC_E_OK: | 168 case SEC_E_OK: |
172 return OK; | 169 return OK; |
173 default: | 170 default: |
174 // The documentation at | 171 // The documentation at |
175 // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx | 172 // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx |
176 // only mentions that a non-zero (or non-SEC_E_OK) value is returned | 173 // only mentions that a non-zero (or non-SEC_E_OK) value is returned |
177 // if the function fails, and does not indicate what the failure | 174 // if the function fails, and does not indicate what the failure |
178 // conditions are. | 175 // conditions are. |
179 LOG(WARNING) | 176 LOG(WARNING) << "FreeContextBuffer returned undocumented status 0x" |
180 << "FreeContextBuffer returned undocumented status 0x" | 177 << std::hex << status; |
181 << std::hex << status; | |
182 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; | 178 return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
183 } | 179 } |
184 } | 180 } |
185 | 181 |
186 } // anonymous namespace | 182 } // anonymous namespace |
187 | 183 |
188 HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library, | 184 HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library, |
189 const std::string& scheme, | 185 const std::string& scheme, |
190 const SEC_WCHAR* security_package, | 186 const SEC_WCHAR* security_package, |
191 ULONG max_token_length) | 187 ULONG max_token_length) |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 int rv = OnFirstRound(credentials); | 259 int rv = OnFirstRound(credentials); |
264 if (rv != OK) | 260 if (rv != OK) |
265 return rv; | 261 return rv; |
266 } | 262 } |
267 | 263 |
268 DCHECK(SecIsValidHandle(&cred_)); | 264 DCHECK(SecIsValidHandle(&cred_)); |
269 void* out_buf; | 265 void* out_buf; |
270 int out_buf_len; | 266 int out_buf_len; |
271 int rv = GetNextSecurityToken( | 267 int rv = GetNextSecurityToken( |
272 spn, | 268 spn, |
273 static_cast<void *>(const_cast<char *>( | 269 static_cast<void*>(const_cast<char*>(decoded_server_auth_token_.c_str())), |
274 decoded_server_auth_token_.c_str())), | |
275 decoded_server_auth_token_.length(), | 270 decoded_server_auth_token_.length(), |
276 &out_buf, | 271 &out_buf, |
277 &out_buf_len); | 272 &out_buf_len); |
278 if (rv != OK) | 273 if (rv != OK) |
279 return rv; | 274 return rv; |
280 | 275 |
281 // Base64 encode data in output buffer and prepend the scheme. | 276 // Base64 encode data in output buffer and prepend the scheme. |
282 std::string encode_input(static_cast<char*>(out_buf), out_buf_len); | 277 std::string encode_input(static_cast<char*>(out_buf), out_buf_len); |
283 std::string encode_output; | 278 std::string encode_output; |
284 base::Base64Encode(encode_input, &encode_output); | 279 base::Base64Encode(encode_input, &encode_output); |
285 // OK, we are done with |out_buf| | 280 // OK, we are done with |out_buf| |
286 free(out_buf); | 281 free(out_buf); |
287 *auth_token = scheme_ + " " + encode_output; | 282 *auth_token = scheme_ + " " + encode_output; |
288 return OK; | 283 return OK; |
289 } | 284 } |
290 | 285 |
291 int HttpAuthSSPI::OnFirstRound(const AuthCredentials* credentials) { | 286 int HttpAuthSSPI::OnFirstRound(const AuthCredentials* credentials) { |
292 DCHECK(!SecIsValidHandle(&cred_)); | 287 DCHECK(!SecIsValidHandle(&cred_)); |
293 int rv = OK; | 288 int rv = OK; |
294 if (credentials) { | 289 if (credentials) { |
295 base::string16 domain; | 290 base::string16 domain; |
296 base::string16 user; | 291 base::string16 user; |
297 SplitDomainAndUser(credentials->username(), &domain, &user); | 292 SplitDomainAndUser(credentials->username(), &domain, &user); |
298 rv = AcquireExplicitCredentials(library_, security_package_, domain, | 293 rv = AcquireExplicitCredentials(library_, |
299 user, credentials->password(), &cred_); | 294 security_package_, |
| 295 domain, |
| 296 user, |
| 297 credentials->password(), |
| 298 &cred_); |
300 if (rv != OK) | 299 if (rv != OK) |
301 return rv; | 300 return rv; |
302 } else { | 301 } else { |
303 rv = AcquireDefaultCredentials(library_, security_package_, &cred_); | 302 rv = AcquireDefaultCredentials(library_, security_package_, &cred_); |
304 if (rv != OK) | 303 if (rv != OK) |
305 return rv; | 304 return rv; |
306 } | 305 } |
307 | 306 |
308 return rv; | 307 return rv; |
309 } | 308 } |
310 | 309 |
311 int HttpAuthSSPI::GetNextSecurityToken( | 310 int HttpAuthSSPI::GetNextSecurityToken(const std::string& spn, |
312 const std::string& spn, | 311 const void* in_token, |
313 const void* in_token, | 312 int in_token_len, |
314 int in_token_len, | 313 void** out_token, |
315 void** out_token, | 314 int* out_token_len) { |
316 int* out_token_len) { | |
317 CtxtHandle* ctxt_ptr; | 315 CtxtHandle* ctxt_ptr; |
318 SecBufferDesc in_buffer_desc, out_buffer_desc; | 316 SecBufferDesc in_buffer_desc, out_buffer_desc; |
319 SecBufferDesc* in_buffer_desc_ptr; | 317 SecBufferDesc* in_buffer_desc_ptr; |
320 SecBuffer in_buffer, out_buffer; | 318 SecBuffer in_buffer, out_buffer; |
321 | 319 |
322 if (in_token_len > 0) { | 320 if (in_token_len > 0) { |
323 // Prepare input buffer. | 321 // Prepare input buffer. |
324 in_buffer_desc.ulVersion = SECBUFFER_VERSION; | 322 in_buffer_desc.ulVersion = SECBUFFER_VERSION; |
325 in_buffer_desc.cBuffers = 1; | 323 in_buffer_desc.cBuffers = 1; |
326 in_buffer_desc.pBuffers = &in_buffer; | 324 in_buffer_desc.pBuffers = &in_buffer; |
(...skipping 27 matching lines...) Expand all Loading... |
354 DWORD context_flags = 0; | 352 DWORD context_flags = 0; |
355 // Firefox only sets ISC_REQ_DELEGATE, but MSDN documentation indicates that | 353 // Firefox only sets ISC_REQ_DELEGATE, but MSDN documentation indicates that |
356 // ISC_REQ_MUTUAL_AUTH must also be set. | 354 // ISC_REQ_MUTUAL_AUTH must also be set. |
357 if (can_delegate_) | 355 if (can_delegate_) |
358 context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH); | 356 context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH); |
359 | 357 |
360 // This returns a token that is passed to the remote server. | 358 // This returns a token that is passed to the remote server. |
361 DWORD context_attribute; | 359 DWORD context_attribute; |
362 std::wstring spn_wide = base::ASCIIToWide(spn); | 360 std::wstring spn_wide = base::ASCIIToWide(spn); |
363 SECURITY_STATUS status = library_->InitializeSecurityContext( | 361 SECURITY_STATUS status = library_->InitializeSecurityContext( |
364 &cred_, // phCredential | 362 &cred_, // phCredential |
365 ctxt_ptr, // phContext | 363 ctxt_ptr, // phContext |
366 const_cast<wchar_t *>(spn_wide.c_str()), // pszTargetName | 364 const_cast<wchar_t*>(spn_wide.c_str()), // pszTargetName |
367 context_flags, // fContextReq | 365 context_flags, // fContextReq |
368 0, // Reserved1 (must be 0) | 366 0, // Reserved1 (must be 0) |
369 SECURITY_NATIVE_DREP, // TargetDataRep | 367 SECURITY_NATIVE_DREP, // TargetDataRep |
370 in_buffer_desc_ptr, // pInput | 368 in_buffer_desc_ptr, // pInput |
371 0, // Reserved2 (must be 0) | 369 0, // Reserved2 (must be 0) |
372 &ctxt_, // phNewContext | 370 &ctxt_, // phNewContext |
373 &out_buffer_desc, // pOutput | 371 &out_buffer_desc, // pOutput |
374 &context_attribute, // pfContextAttr | 372 &context_attribute, // pfContextAttr |
375 NULL); // ptsExpiry | 373 NULL); // ptsExpiry |
376 int rv = MapInitializeSecurityContextStatusToError(status); | 374 int rv = MapInitializeSecurityContextStatusToError(status); |
377 if (rv != OK) { | 375 if (rv != OK) { |
378 ResetSecurityContext(); | 376 ResetSecurityContext(); |
379 free(out_buffer.pvBuffer); | 377 free(out_buffer.pvBuffer); |
380 return rv; | 378 return rv; |
381 } | 379 } |
382 if (!out_buffer.cbBuffer) { | 380 if (!out_buffer.cbBuffer) { |
383 free(out_buffer.pvBuffer); | 381 free(out_buffer.pvBuffer); |
384 out_buffer.pvBuffer = NULL; | 382 out_buffer.pvBuffer = NULL; |
385 } | 383 } |
(...skipping 18 matching lines...) Expand all Loading... |
404 } | 402 } |
405 } | 403 } |
406 | 404 |
407 int DetermineMaxTokenLength(SSPILibrary* library, | 405 int DetermineMaxTokenLength(SSPILibrary* library, |
408 const std::wstring& package, | 406 const std::wstring& package, |
409 ULONG* max_token_length) { | 407 ULONG* max_token_length) { |
410 DCHECK(library); | 408 DCHECK(library); |
411 DCHECK(max_token_length); | 409 DCHECK(max_token_length); |
412 PSecPkgInfo pkg_info = NULL; | 410 PSecPkgInfo pkg_info = NULL; |
413 SECURITY_STATUS status = library->QuerySecurityPackageInfo( | 411 SECURITY_STATUS status = library->QuerySecurityPackageInfo( |
414 const_cast<wchar_t *>(package.c_str()), &pkg_info); | 412 const_cast<wchar_t*>(package.c_str()), &pkg_info); |
415 int rv = MapQuerySecurityPackageInfoStatusToError(status); | 413 int rv = MapQuerySecurityPackageInfoStatusToError(status); |
416 if (rv != OK) | 414 if (rv != OK) |
417 return rv; | 415 return rv; |
418 int token_length = pkg_info->cbMaxToken; | 416 int token_length = pkg_info->cbMaxToken; |
419 status = library->FreeContextBuffer(pkg_info); | 417 status = library->FreeContextBuffer(pkg_info); |
420 rv = MapFreeContextBufferStatusToError(status); | 418 rv = MapFreeContextBufferStatusToError(status); |
421 if (rv != OK) | 419 if (rv != OK) |
422 return rv; | 420 return rv; |
423 *max_token_length = token_length; | 421 *max_token_length = token_length; |
424 return OK; | 422 return OK; |
425 } | 423 } |
426 | 424 |
427 } // namespace net | 425 } // namespace net |
OLD | NEW |