1 /*
2 Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29
30 #include "precompiled.hpp"
31
32 #ifdef HAVE_LIBGSSAPI_KRB5
33
34 #include <string.h>
35 #include <string>
36
37 #include "msg.hpp"
38 #include "session_base.hpp"
39 #include "err.hpp"
40 #include "gssapi_client.hpp"
41 #include "wire.hpp"
42
gssapi_client_t(session_base_t * session_,const options_t & options_)43 zmq::gssapi_client_t::gssapi_client_t (session_base_t *session_,
44 const options_t &options_) :
45 mechanism_base_t (session_, options_),
46 gssapi_mechanism_base_t (session_, options_),
47 state (call_next_init),
48 token_ptr (GSS_C_NO_BUFFER),
49 mechs (),
50 security_context_established (false)
51 {
52 const std::string::size_type service_size =
53 options_.gss_service_principal.size ();
54 service_name = static_cast<char *> (malloc (service_size + 1));
55 assert (service_name);
56 memcpy (service_name, options_.gss_service_principal.c_str (),
57 service_size + 1);
58
59 service_name_type = convert_nametype (options_.gss_service_principal_nt);
60 maj_stat = GSS_S_COMPLETE;
61 if (!options_.gss_principal.empty ()) {
62 const std::string::size_type principal_size =
63 options_.gss_principal.size ();
64 principal_name = static_cast<char *> (malloc (principal_size + 1));
65 assert (principal_name);
66 memcpy (principal_name, options_.gss_principal.c_str (),
67 principal_size + 1);
68
69 gss_OID name_type = convert_nametype (options_.gss_principal_nt);
70 if (acquire_credentials (principal_name, &cred, name_type) != 0)
71 maj_stat = GSS_S_FAILURE;
72 }
73
74 mechs.elements = NULL;
75 mechs.count = 0;
76 }
77
~gssapi_client_t()78 zmq::gssapi_client_t::~gssapi_client_t ()
79 {
80 if (service_name)
81 free (service_name);
82 if (cred)
83 gss_release_cred (&min_stat, &cred);
84 }
85
next_handshake_command(msg_t * msg_)86 int zmq::gssapi_client_t::next_handshake_command (msg_t *msg_)
87 {
88 if (state == send_ready) {
89 int rc = produce_ready (msg_);
90 if (rc == 0)
91 state = connected;
92
93 return rc;
94 }
95
96 if (state != call_next_init) {
97 errno = EAGAIN;
98 return -1;
99 }
100
101 if (initialize_context () < 0)
102 return -1;
103
104 if (produce_next_token (msg_) < 0)
105 return -1;
106
107 if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE)
108 return -1;
109
110 if (maj_stat == GSS_S_COMPLETE) {
111 security_context_established = true;
112 state = recv_ready;
113 } else
114 state = recv_next_token;
115
116 return 0;
117 }
118
process_handshake_command(msg_t * msg_)119 int zmq::gssapi_client_t::process_handshake_command (msg_t *msg_)
120 {
121 if (state == recv_ready) {
122 int rc = process_ready (msg_);
123 if (rc == 0)
124 state = send_ready;
125
126 return rc;
127 }
128
129 if (state != recv_next_token) {
130 session->get_socket ()->event_handshake_failed_protocol (
131 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
132 errno = EPROTO;
133 return -1;
134 }
135
136 if (process_next_token (msg_) < 0)
137 return -1;
138
139 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
140 return -1;
141
142 state = call_next_init;
143
144 errno_assert (msg_->close () == 0);
145 errno_assert (msg_->init () == 0);
146
147 return 0;
148 }
149
encode(msg_t * msg_)150 int zmq::gssapi_client_t::encode (msg_t *msg_)
151 {
152 zmq_assert (state == connected);
153
154 if (do_encryption)
155 return encode_message (msg_);
156
157 return 0;
158 }
159
decode(msg_t * msg_)160 int zmq::gssapi_client_t::decode (msg_t *msg_)
161 {
162 zmq_assert (state == connected);
163
164 if (do_encryption)
165 return decode_message (msg_);
166
167 return 0;
168 }
169
status() const170 zmq::mechanism_t::status_t zmq::gssapi_client_t::status () const
171 {
172 return state == connected ? mechanism_t::ready : mechanism_t::handshaking;
173 }
174
initialize_context()175 int zmq::gssapi_client_t::initialize_context ()
176 {
177 // principal was specified but credentials could not be acquired
178 if (principal_name != NULL && cred == NULL)
179 return -1;
180
181 // First time through, import service_name into target_name
182 if (target_name == GSS_C_NO_NAME) {
183 send_tok.value = service_name;
184 send_tok.length = strlen (service_name) + 1;
185 OM_uint32 maj = gss_import_name (&min_stat, &send_tok,
186 service_name_type, &target_name);
187
188 if (maj != GSS_S_COMPLETE)
189 return -1;
190 }
191
192 maj_stat = gss_init_sec_context (
193 &init_sec_min_stat, cred, &context, target_name, mechs.elements,
194 gss_flags, 0, NULL, token_ptr, NULL, &send_tok, &ret_flags, NULL);
195
196 if (token_ptr != GSS_C_NO_BUFFER)
197 free (recv_tok.value);
198
199 return 0;
200 }
201
produce_next_token(msg_t * msg_)202 int zmq::gssapi_client_t::produce_next_token (msg_t *msg_)
203 {
204 if (send_tok.length != 0) { // Server expects another token
205 if (produce_initiate (msg_, send_tok.value, send_tok.length) < 0) {
206 gss_release_buffer (&min_stat, &send_tok);
207 gss_release_name (&min_stat, &target_name);
208 return -1;
209 }
210 }
211 gss_release_buffer (&min_stat, &send_tok);
212
213 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
214 gss_release_name (&min_stat, &target_name);
215 if (context != GSS_C_NO_CONTEXT)
216 gss_delete_sec_context (&min_stat, &context, GSS_C_NO_BUFFER);
217 return -1;
218 }
219
220 return 0;
221 }
222
process_next_token(msg_t * msg_)223 int zmq::gssapi_client_t::process_next_token (msg_t *msg_)
224 {
225 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
226 if (process_initiate (msg_, &recv_tok.value, recv_tok.length) < 0) {
227 gss_release_name (&min_stat, &target_name);
228 return -1;
229 }
230 token_ptr = &recv_tok;
231 }
232
233 return 0;
234 }
235
236 #endif
237