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