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