1 /* Copyright (C) 2008 CERN
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License
5  * as published by the Free Software Foundation; either version 2
6  * of the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
16  * USA.
17  */
18 
19 /* Author: Ian Baker */
20 
21 #include <config.h>
22 
23 #ifdef HAVE_GSSAPI
24 #include <arpa/inet.h>
25 #endif
26 
27 #include <limits.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "auth.h"
33 #include "distcc.h"
34 #include "exitcode.h"
35 #include "netutil.h"
36 #include "rpc.h"
37 #include "trace.h"
38 
39 /*
40  * Provide a textual representation of a GSS-API or mechanism-specific
41  * status code and write this to the log file.
42  *
43  * @param status_code.	Status code to convert.
44  *
45  * @param status_type.	The type of the status code, either GSS_C_GSS_CODE
46  *			for a GSS-API status code or GSS_C_MECH_CODE
47  *			for a mechanism-specific status code.
48  */
dcc_gssapi_status_to_log(OM_uint32 status_code,int status_type)49 void dcc_gssapi_status_to_log(OM_uint32 status_code, int status_type) {
50     gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
51     OM_uint32 major_status, minor_status;
52     OM_uint32 message_ctx = 0;
53 
54     do {
55         major_status = gss_display_status(&minor_status,
56 					  status_code,
57 					  status_type,
58 					  GSS_C_NULL_OID,
59 					  &message_ctx,
60 					  &status_string);
61 
62 	rs_log_info("%s: %s", (status_type == GSS_C_GSS_CODE) ? "GSSAPI" : "Mech" ,
63 					  (char *) status_string.value);
64         gss_release_buffer(&minor_status, &status_string);
65 
66     } while (message_ctx != 0);
67 
68     (void) major_status;
69     (void) minor_status;
70 }
71 
72 /*
73  * Perform a cleanup on specified GSS-API internal data formats.
74  *
75  * @param input_tok.	The input token to release.
76  *
77  * @param output_tok.	The output token to release.
78  *
79  * @param name.		The name to release.
80  */
dcc_gssapi_cleanup(gss_buffer_desc * input_tok,gss_buffer_desc * output_tok,gss_name_t * name)81 void dcc_gssapi_cleanup(gss_buffer_desc *input_tok,
82 			gss_buffer_desc *output_tok,
83 			gss_name_t *name) {
84     OM_uint32 major_status, minor_status;
85 
86     if (input_tok != NULL) {
87         if ((major_status = gss_release_buffer(&minor_status,
88 					      input_tok)) != GSS_S_COMPLETE) {
89             rs_log_error("Failed to release buffer.");
90         }
91     }
92 
93     if (output_tok != NULL) {
94         if ((major_status = gss_release_buffer(&minor_status,
95 					      output_tok)) != GSS_S_COMPLETE) {
96             rs_log_error("Failed to release buffer.");
97         }
98     }
99 
100     if (name != NULL) {
101         if ((major_status = gss_release_name(&minor_status,
102 					    name)) != GSS_S_COMPLETE) {
103             rs_log_error("Failed to release name.");
104         }
105     }
106 
107     (void) major_status;
108     (void) minor_status;
109 }
110 
111 /*
112  * Perform a comparison of two sets of flags representing security
113  * services.  At the moment we only check for mutual authentication,
114  * replay and out of sequence detection, but this function could be
115  * extended to include others.
116  *
117  * @param req_flags.	The first set of flags to be compared and
118  *                      represents what is required by the client
119  *                      or server.
120  *
121  * @param ret_flags.	The second set of flags to be compared and
122  *                      what is provided/returned by the peer.
123  *
124  * Returns 0 on success, otherwise error.
125  */
dcc_gssapi_compare_flags(OM_uint32 req_flags,OM_uint32 ret_flags)126 int dcc_gssapi_compare_flags(OM_uint32 req_flags, OM_uint32 ret_flags) {
127     if (req_flags & GSS_C_MUTUAL_FLAG) {
128         if (ret_flags & GSS_C_MUTUAL_FLAG) {
129             rs_log_info("Mutual authentication requested and granted.");
130         } else {
131             rs_log_crit("Requested security services don't match those returned.");
132             return EXIT_GSSAPI_FAILED;
133         }
134     }
135 
136     if (req_flags & GSS_C_REPLAY_FLAG) {
137         if (ret_flags & GSS_C_REPLAY_FLAG) {
138             rs_log_info("Replay detection requested and granted.");
139         } else {
140             rs_log_crit("Requested security services don't match those returned.");
141             return EXIT_GSSAPI_FAILED;
142         }
143     }
144 
145     if (req_flags & GSS_C_SEQUENCE_FLAG) {
146         if (ret_flags & GSS_C_SEQUENCE_FLAG) {
147             rs_log_info("Out of sequence detection requested and granted.");
148         } else {
149             rs_log_crit("Requested security services don't match those returned.");
150             return EXIT_GSSAPI_FAILED;
151         }
152     }
153 
154     return 0;
155 }
156 
157 /*
158  * Delete a specified secure context from the current process.
159  * The output buffer token is not used as we do not notify the
160  * peer.
161  *
162  * @param ctx_handle.	The secure context to delete.
163  */
dcc_gssapi_delete_ctx(gss_ctx_id_t * ctx_handle)164 void dcc_gssapi_delete_ctx(gss_ctx_id_t *ctx_handle) {
165     OM_uint32 major_status, minor_status;
166 
167     if (ctx_handle != NULL) {
168         if (*ctx_handle != GSS_C_NO_CONTEXT) {
169             if ((major_status = gss_delete_sec_context(&minor_status,
170 					ctx_handle,
171 					GSS_C_NO_BUFFER)) != GSS_S_COMPLETE) {
172                 rs_log_error("Failed to delete context.");
173             }
174         }
175     }
176 }
177 
178 /*
179  * Send the specified token.  First we transmit the token length,
180  * then the token itself.
181  *
182  * @param token.	The token to be sent.
183  *
184  * Returns 0 on success, otherwise error.
185  */
send_token(int sd,gss_buffer_t token)186 int send_token(int sd, gss_buffer_t token) {
187     int ret;
188 
189     if ((ret = dcc_x_token_int(sd, "TLEN", token->length)) != 0) {
190 	return ret;
191     }
192 
193     if ((ret = dcc_writex(sd, token->value, token->length)) != 0) {
194         return ret;
195     }
196 
197     return 0;
198 }
199 
200 /*
201  * Receive a token.  First we receive the token length,
202  * then the token itself.
203  *
204  * @param token.	The token to be received.
205  *
206  * Returns 0 on success, otherwise error.
207  */
recv_token(int sd,gss_buffer_t token)208 int recv_token(int sd, gss_buffer_t token) {
209     int ret;
210     unsigned length;
211 
212     if ((ret = dcc_r_token_int(sd, "TLEN", &length)) != 0) {
213         return ret;
214     }
215 
216     if (length > INT_MAX || length == 0) {
217 	rs_log_error("Malformed token length.");
218 	return EXIT_IO_ERROR;
219     }
220 
221     token->length = length;
222     token->value = malloc(length);
223 
224     if (token->value == NULL && length != 0) {
225 	    rs_log_error("malloc failed : %lu bytes: out of memory.",
226 	                                    (unsigned long) length);
227         return EXIT_OUT_OF_MEMORY;
228     }
229 
230     if ((ret = dcc_readx(sd, token->value, token->length)) != 0) {
231         return ret;
232     }
233 
234     return 0;
235 }
236