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