1 | /******************************************************************** |
2 | * Copyright (c) 2013 - 2014, Pivotal Inc. |
3 | * All rights reserved. |
4 | * |
5 | * Author: Zhanwei Wang |
6 | ********************************************************************/ |
7 | /******************************************************************** |
8 | * 2014 - |
9 | * open source under Apache License Version 2.0 |
10 | ********************************************************************/ |
11 | /** |
12 | * Licensed to the Apache Software Foundation (ASF) under one |
13 | * or more contributor license agreements. See the NOTICE file |
14 | * distributed with this work for additional information |
15 | * regarding copyright ownership. The ASF licenses this file |
16 | * to you under the Apache License, Version 2.0 (the |
17 | * "License"); you may not use this file except in compliance |
18 | * with the License. You may obtain a copy of the License at |
19 | * |
20 | * http://www.apache.org/licenses/LICENSE-2.0 |
21 | * |
22 | * Unless required by applicable law or agreed to in writing, software |
23 | * distributed under the License is distributed on an "AS IS" BASIS, |
24 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
25 | * See the License for the specific language governing permissions and |
26 | * limitations under the License. |
27 | */ |
28 | #include <algorithm> |
29 | #include <cctype> |
30 | |
31 | #include "Exception.h" |
32 | #include "ExceptionInternal.h" |
33 | #include "SaslClient.h" |
34 | |
35 | #define SASL_SUCCESS 0 |
36 | |
37 | namespace Hdfs { |
38 | namespace Internal { |
39 | |
40 | SaslClient::SaslClient(const RpcSaslProto_SaslAuth & auth, const Token & token, |
41 | const std::string & principal) : |
42 | complete(false) { |
43 | int rc; |
44 | ctx = NULL; |
45 | RpcAuth method = RpcAuth(RpcAuth::ParseMethod(auth.method())); |
46 | rc = gsasl_init(&ctx); |
47 | |
48 | if (rc != GSASL_OK) { |
49 | THROW(HdfsIOException, "cannot initialize libgsasl" ); |
50 | } |
51 | |
52 | switch (method.getMethod()) { |
53 | case AuthMethod::KERBEROS: |
54 | initKerberos(auth, principal); |
55 | break; |
56 | |
57 | case AuthMethod::TOKEN: |
58 | initDigestMd5(auth, token); |
59 | break; |
60 | |
61 | default: |
62 | THROW(HdfsIOException, "unknown auth method." ); |
63 | break; |
64 | } |
65 | } |
66 | |
67 | SaslClient::~SaslClient() { |
68 | if (session != NULL) { |
69 | gsasl_finish(session); |
70 | } |
71 | |
72 | if (ctx != NULL) { |
73 | gsasl_done(ctx); |
74 | } |
75 | } |
76 | |
77 | void SaslClient::initKerberos(const RpcSaslProto_SaslAuth & auth, |
78 | const std::string & principal) { |
79 | int rc; |
80 | |
81 | /* Create new authentication session. */ |
82 | if ((rc = gsasl_client_start(ctx, auth.mechanism().c_str(), &session)) != GSASL_OK) { |
83 | THROW(HdfsIOException, "Cannot initialize client (%d): %s" , rc, |
84 | gsasl_strerror(rc)); |
85 | } |
86 | |
87 | gsasl_property_set(session, GSASL_SERVICE, auth.protocol().c_str()); |
88 | gsasl_property_set(session, GSASL_AUTHID, principal.c_str()); |
89 | gsasl_property_set(session, GSASL_HOSTNAME, auth.serverid().c_str()); |
90 | } |
91 | |
92 | std::string Base64Encode(const std::string & in) { |
93 | char * temp; |
94 | size_t len; |
95 | std::string retval; |
96 | int rc = gsasl_base64_to(in.c_str(), in.size(), &temp, &len); |
97 | |
98 | if (rc != GSASL_OK) { |
99 | throw std::bad_alloc(); |
100 | } |
101 | |
102 | if (temp) { |
103 | retval = temp; |
104 | free(temp); |
105 | } |
106 | |
107 | if (!temp || retval.length() != len) { |
108 | THROW(HdfsIOException, "SaslClient: Failed to encode string to base64" ); |
109 | } |
110 | |
111 | return retval; |
112 | } |
113 | |
114 | void SaslClient::initDigestMd5(const RpcSaslProto_SaslAuth & auth, |
115 | const Token & token) { |
116 | int rc; |
117 | |
118 | if ((rc = gsasl_client_start(ctx, auth.mechanism().c_str(), &session)) != GSASL_OK) { |
119 | THROW(HdfsIOException, "Cannot initialize client (%d): %s" , rc, gsasl_strerror(rc)); |
120 | } |
121 | |
122 | std::string password = Base64Encode(token.getPassword()); |
123 | std::string identifier = Base64Encode(token.getIdentifier()); |
124 | gsasl_property_set(session, GSASL_PASSWORD, password.c_str()); |
125 | gsasl_property_set(session, GSASL_AUTHID, identifier.c_str()); |
126 | gsasl_property_set(session, GSASL_HOSTNAME, auth.serverid().c_str()); |
127 | gsasl_property_set(session, GSASL_SERVICE, auth.protocol().c_str()); |
128 | } |
129 | |
130 | std::string SaslClient::evaluateChallenge(const std::string & challenge) { |
131 | int rc; |
132 | char * output = NULL; |
133 | size_t outputSize; |
134 | std::string retval; |
135 | rc = gsasl_step(session, &challenge[0], challenge.size(), &output, |
136 | &outputSize); |
137 | |
138 | if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK) { |
139 | retval.resize(outputSize); |
140 | memcpy(&retval[0], output, outputSize); |
141 | |
142 | if (output) { |
143 | free(output); |
144 | } |
145 | } else { |
146 | if (output) { |
147 | free(output); |
148 | } |
149 | |
150 | THROW(AccessControlException, "Failed to evaluate challenge: %s" , gsasl_strerror(rc)); |
151 | } |
152 | |
153 | if (rc == GSASL_OK) { |
154 | complete = true; |
155 | } |
156 | |
157 | return retval; |
158 | } |
159 | |
160 | bool SaslClient::isComplete() { |
161 | return complete; |
162 | } |
163 | |
164 | } |
165 | } |
166 | |
167 | |