xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/krb5/sendauth.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1 /*	$NetBSD: sendauth.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "krb5_locl.h"
37 
38 /*
39  * The format seems to be:
40  * client -> server
41  *
42  * 4 bytes - length
43  * KRB5_SENDAUTH_V1.0 (including zero)
44  * 4 bytes - length
45  * protocol string (with terminating zero)
46  *
47  * server -> client
48  * 1 byte - (0 = OK, else some kind of error)
49  *
50  * client -> server
51  * 4 bytes - length
52  * AP-REQ
53  *
54  * server -> client
55  * 4 bytes - length (0 = OK, else length of error)
56  * (error)
57  *
58  * if(mutual) {
59  * server -> client
60  * 4 bytes - length
61  * AP-REP
62  * }
63  */
64 
65 /**
66  * Perform the client side of the sendauth protocol.
67  *
68  * @param context        Kerberos 5 context.
69  * @param auth_context   Authentication context of the peer.
70  * @param p_fd           Socket associated to the connection.
71  * @param appl_version   Server-specific string.
72  * @param client         Client principal. If NULL, use the credentials in \a ccache.
73  * @param server         Server principal.
74  * @param ap_req_options Options for the AP_REQ message. See the AP_OPTS_* defines in krb5.h.
75  * @param in_data        FIXME
76  * @param in_creds       FIXME
77  * @param ccache         Credentials cache. If NULL, use the default credentials cache.
78  * @param ret_error      If not NULL, will be set to the error reported by server, if any.
79  *                       Must be deallocated with krb5_free_error_contents().
80  * @param rep_result     If not NULL, will be set to the EncApRepPart of the AP_REP message.
81  *                       Must be deallocated with krb5_free_ap_rep_enc_part().
82  * @param out_creds      FIXME If not NULL, will be set to FIXME. Must be deallocated with
83  *                       krb5_free_creds().
84  *
85  * @return 0 to indicate success. Otherwise a Kerberos error code is
86  *         returned, see krb5_get_error_message().
87  */
88 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendauth(krb5_context context,krb5_auth_context * auth_context,krb5_pointer p_fd,const char * appl_version,krb5_principal client,krb5_principal server,krb5_flags ap_req_options,krb5_data * in_data,krb5_creds * in_creds,krb5_ccache ccache,krb5_error ** ret_error,krb5_ap_rep_enc_part ** rep_result,krb5_creds ** out_creds)89 krb5_sendauth(krb5_context context,
90 	      krb5_auth_context *auth_context,
91 	      krb5_pointer p_fd,
92 	      const char *appl_version,
93 	      krb5_principal client,
94 	      krb5_principal server,
95 	      krb5_flags ap_req_options,
96 	      krb5_data *in_data,
97 	      krb5_creds *in_creds,
98 	      krb5_ccache ccache,
99 	      krb5_error **ret_error,
100 	      krb5_ap_rep_enc_part **rep_result,
101 	      krb5_creds **out_creds)
102 {
103     krb5_error_code ret;
104     uint32_t len, net_len;
105     const char *version = KRB5_SENDAUTH_VERSION;
106     u_char repl;
107     krb5_data ap_req, error_data;
108     krb5_creds this_cred;
109     krb5_principal this_client = NULL;
110     krb5_creds *creds;
111     ssize_t sret;
112     krb5_boolean my_ccache = FALSE;
113 
114     len = strlen(version) + 1;
115     net_len = htonl(len);
116     if (krb5_net_write (context, p_fd, &net_len, 4) != 4
117 	|| krb5_net_write (context, p_fd, version, len) != len) {
118 	ret = errno;
119 	krb5_set_error_message (context, ret, "write: %s", strerror(ret));
120 	return ret;
121     }
122 
123     len = strlen(appl_version) + 1;
124     net_len = htonl(len);
125     if (krb5_net_write (context, p_fd, &net_len, 4) != 4
126 	|| krb5_net_write (context, p_fd, appl_version, len) != len) {
127 	ret = errno;
128 	krb5_set_error_message (context, ret, "write: %s", strerror(ret));
129 	return ret;
130     }
131 
132     sret = krb5_net_read (context, p_fd, &repl, sizeof(repl));
133     if (sret < 0) {
134 	ret = errno;
135 	krb5_set_error_message (context, ret, "read: %s", strerror(ret));
136 	return ret;
137     } else if (sret != sizeof(repl)) {
138 	krb5_clear_error_message (context);
139 	return KRB5_SENDAUTH_BADRESPONSE;
140     }
141 
142     if (repl != 0) {
143 	krb5_clear_error_message (context);
144 	return KRB5_SENDAUTH_REJECTED;
145     }
146 
147     if (in_creds == NULL) {
148 	if (ccache == NULL) {
149 	    ret = krb5_cc_default (context, &ccache);
150 	    if (ret)
151 		return ret;
152 	    my_ccache = TRUE;
153 	}
154 
155 	if (client == NULL) {
156 	    ret = krb5_cc_get_principal (context, ccache, &this_client);
157 	    if (ret) {
158 		if(my_ccache)
159 		    krb5_cc_close(context, ccache);
160 		return ret;
161 	    }
162 	    client = this_client;
163 	}
164 	memset(&this_cred, 0, sizeof(this_cred));
165 	this_cred.client = client;
166 	this_cred.server = server;
167 	this_cred.times.endtime = 0;
168 	this_cred.ticket.length = 0;
169 	in_creds = &this_cred;
170     }
171     if (in_creds->ticket.length == 0) {
172 	ret = krb5_get_credentials (context, 0, ccache, in_creds, &creds);
173 	if (ret) {
174 	    if(my_ccache)
175 		krb5_cc_close(context, ccache);
176 	    return ret;
177 	}
178     } else {
179 	creds = in_creds;
180     }
181     if(my_ccache)
182 	krb5_cc_close(context, ccache);
183     ret = krb5_mk_req_extended (context,
184 				auth_context,
185 				ap_req_options,
186 				in_data,
187 				creds,
188 				&ap_req);
189 
190     if (out_creds)
191 	*out_creds = creds;
192     else
193 	krb5_free_creds(context, creds);
194     if(this_client)
195 	krb5_free_principal(context, this_client);
196 
197     if (ret)
198 	return ret;
199 
200     ret = krb5_write_message (context,
201 			      p_fd,
202 			      &ap_req);
203     if (ret)
204 	return ret;
205 
206     krb5_data_free (&ap_req);
207 
208     ret = krb5_read_message (context, p_fd, &error_data);
209     if (ret)
210 	return ret;
211 
212     if (error_data.length != 0) {
213 	KRB_ERROR error;
214 
215 	ret = krb5_rd_error (context, &error_data, &error);
216 	krb5_data_free (&error_data);
217 	if (ret == 0) {
218 	    ret = krb5_error_from_rd_error(context, &error, NULL);
219 	    if (ret_error != NULL) {
220 		*ret_error = malloc (sizeof(krb5_error));
221 		if (*ret_error == NULL) {
222 		    krb5_free_error_contents (context, &error);
223 		} else {
224 		    **ret_error = error;
225 		}
226 	    } else {
227 		krb5_free_error_contents (context, &error);
228 	    }
229 	    return ret;
230 	} else {
231 	    krb5_clear_error_message(context);
232 	    return ret;
233 	}
234     } else
235 	krb5_data_free (&error_data);
236 
237     if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
238 	krb5_data ap_rep;
239 	krb5_ap_rep_enc_part *ignore = NULL;
240 
241 	krb5_data_zero (&ap_rep);
242 	ret = krb5_read_message (context,
243 				 p_fd,
244 				 &ap_rep);
245 	if (ret)
246 	    return ret;
247 
248 	ret = krb5_rd_rep (context, *auth_context, &ap_rep,
249 			   rep_result ? rep_result : &ignore);
250 	krb5_data_free (&ap_rep);
251 	if (ret)
252 	    return ret;
253 	if (rep_result == NULL)
254 	    krb5_free_ap_rep_enc_part (context, ignore);
255     }
256     return 0;
257 }
258