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_server.hpp"
41 #include "wire.hpp"
42
43 #include <gssapi/gssapi.h>
44
gssapi_server_t(session_base_t * session_,const std::string & peer_address_,const options_t & options_)45 zmq::gssapi_server_t::gssapi_server_t (session_base_t *session_,
46 const std::string &peer_address_,
47 const options_t &options_) :
48 mechanism_base_t (session_, options_),
49 gssapi_mechanism_base_t (session_, options_),
50 zap_client_t (session_, peer_address_, options_),
51 session (session_),
52 peer_address (peer_address_),
53 state (recv_next_token),
54 security_context_established (false)
55 {
56 maj_stat = GSS_S_CONTINUE_NEEDED;
57 if (!options_.gss_principal.empty ()) {
58 const std::string::size_type principal_size =
59 options_.gss_principal.size ();
60 principal_name = static_cast<char *> (malloc (principal_size + 1));
61 assert (principal_name);
62 memcpy (principal_name, options_.gss_principal.c_str (),
63 principal_size + 1);
64 gss_OID name_type = convert_nametype (options_.gss_principal_nt);
65 if (acquire_credentials (principal_name, &cred, name_type) != 0)
66 maj_stat = GSS_S_FAILURE;
67 }
68 }
69
~gssapi_server_t()70 zmq::gssapi_server_t::~gssapi_server_t ()
71 {
72 if (cred)
73 gss_release_cred (&min_stat, &cred);
74
75 if (target_name)
76 gss_release_name (&min_stat, &target_name);
77 }
78
next_handshake_command(msg_t * msg_)79 int zmq::gssapi_server_t::next_handshake_command (msg_t *msg_)
80 {
81 if (state == send_ready) {
82 int rc = produce_ready (msg_);
83 if (rc == 0)
84 state = recv_ready;
85
86 return rc;
87 }
88
89 if (state != send_next_token) {
90 errno = EAGAIN;
91 return -1;
92 }
93
94 if (produce_next_token (msg_) < 0)
95 return -1;
96
97 if (maj_stat != GSS_S_CONTINUE_NEEDED && maj_stat != GSS_S_COMPLETE)
98 return -1;
99
100 if (maj_stat == GSS_S_COMPLETE) {
101 security_context_established = true;
102 }
103
104 state = recv_next_token;
105
106 return 0;
107 }
108
process_handshake_command(msg_t * msg_)109 int zmq::gssapi_server_t::process_handshake_command (msg_t *msg_)
110 {
111 if (state == recv_ready) {
112 int rc = process_ready (msg_);
113 if (rc == 0)
114 state = connected;
115
116 return rc;
117 }
118
119 if (state != recv_next_token) {
120 session->get_socket ()->event_handshake_failed_protocol (
121 session->get_endpoint (), ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
122 errno = EPROTO;
123 return -1;
124 }
125
126 if (security_context_established) {
127 // Use ZAP protocol (RFC 27) to authenticate the user.
128 // Note that rc will be -1 only if ZAP is not set up, but if it was
129 // requested and it does not work properly the program will abort.
130 bool expecting_zap_reply = false;
131 int rc = session->zap_connect ();
132 if (rc == 0) {
133 send_zap_request ();
134 rc = receive_and_process_zap_reply ();
135 if (rc != 0) {
136 if (rc == -1)
137 return -1;
138 expecting_zap_reply = true;
139 }
140 }
141 state = expecting_zap_reply ? expect_zap_reply : send_ready;
142 return 0;
143 }
144
145 if (process_next_token (msg_) < 0)
146 return -1;
147
148 accept_context ();
149 state = send_next_token;
150
151 errno_assert (msg_->close () == 0);
152 errno_assert (msg_->init () == 0);
153
154 return 0;
155 }
156
send_zap_request()157 void zmq::gssapi_server_t::send_zap_request ()
158 {
159 gss_buffer_desc principal;
160 gss_display_name (&min_stat, target_name, &principal, NULL);
161 zap_client_t::send_zap_request (
162 "GSSAPI", 6, reinterpret_cast<const uint8_t *> (principal.value),
163 principal.length);
164
165 gss_release_buffer (&min_stat, &principal);
166 }
167
encode(msg_t * msg_)168 int zmq::gssapi_server_t::encode (msg_t *msg_)
169 {
170 zmq_assert (state == connected);
171
172 if (do_encryption)
173 return encode_message (msg_);
174
175 return 0;
176 }
177
decode(msg_t * msg_)178 int zmq::gssapi_server_t::decode (msg_t *msg_)
179 {
180 zmq_assert (state == connected);
181
182 if (do_encryption)
183 return decode_message (msg_);
184
185 return 0;
186 }
187
zap_msg_available()188 int zmq::gssapi_server_t::zap_msg_available ()
189 {
190 if (state != expect_zap_reply) {
191 errno = EFSM;
192 return -1;
193 }
194 const int rc = receive_and_process_zap_reply ();
195 if (rc == 0)
196 state = send_ready;
197 return rc == -1 ? -1 : 0;
198 }
199
status() const200 zmq::mechanism_t::status_t zmq::gssapi_server_t::status () const
201 {
202 return state == connected ? mechanism_t::ready : mechanism_t::handshaking;
203 }
204
produce_next_token(msg_t * msg_)205 int zmq::gssapi_server_t::produce_next_token (msg_t *msg_)
206 {
207 if (send_tok.length != 0) { // Client expects another token
208 if (produce_initiate (msg_, send_tok.value, send_tok.length) < 0)
209 return -1;
210 gss_release_buffer (&min_stat, &send_tok);
211 }
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_server_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 if (target_name != GSS_C_NO_NAME)
228 gss_release_name (&min_stat, &target_name);
229 return -1;
230 }
231 }
232
233 return 0;
234 }
235
accept_context()236 void zmq::gssapi_server_t::accept_context ()
237 {
238 maj_stat = gss_accept_sec_context (
239 &init_sec_min_stat, &context, cred, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
240 &target_name, &doid, &send_tok, &ret_flags, NULL, NULL);
241
242 if (recv_tok.value) {
243 free (recv_tok.value);
244 recv_tok.value = NULL;
245 }
246 }
247
248 #endif
249