1/* $NetBSD: recvauth.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 3/* 4 * Copyright (c) 1997-2007 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 * See `sendauth.c' for the format. 40 */ 41 42static krb5_boolean 43match_exact(const void *data, const char *appl_version) 44{ 45 return strcmp(data, appl_version) == 0; 46} 47 48/** 49 * Perform the server side of the sendauth protocol. 50 * 51 * @param context Kerberos 5 context. 52 * @param auth_context authentication context of the peer. 53 * @param p_fd socket associated to the connection. 54 * @param appl_version server-specific string. 55 * @param server server principal. 56 * @param flags if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version 57 * part of the protocol. 58 * @param keytab server keytab. 59 * @param ticket on success, set to the authenticated client credentials. 60 * Must be deallocated with krb5_free_ticket(). If not 61 * interested, pass a NULL value. 62 * 63 * @return 0 to indicate success. Otherwise a Kerberos error code is 64 * returned, see krb5_get_error_message(). 65 */ 66KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 67krb5_recvauth(krb5_context context, 68 krb5_auth_context *auth_context, 69 krb5_pointer p_fd, 70 const char *appl_version, 71 krb5_principal server, 72 int32_t flags, 73 krb5_keytab keytab, 74 krb5_ticket **ticket) 75{ 76 return krb5_recvauth_match_version(context, auth_context, p_fd, 77 match_exact, appl_version, 78 server, flags, 79 keytab, ticket); 80} 81 82/** 83 * Perform the server side of the sendauth protocol like krb5_recvauth(), but support 84 * a user-specified callback, \a match_appl_version, to perform the match of the application 85 * version \a match_data. 86 */ 87KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 88krb5_recvauth_match_version(krb5_context context, 89 krb5_auth_context *auth_context, 90 krb5_pointer p_fd, 91 krb5_boolean (*match_appl_version)(const void *, 92 const char*), 93 const void *match_data, 94 krb5_principal server, 95 int32_t flags, 96 krb5_keytab keytab, 97 krb5_ticket **ticket) 98{ 99 krb5_error_code ret; 100 const char *version = KRB5_SENDAUTH_VERSION; 101 char her_version[sizeof(KRB5_SENDAUTH_VERSION)]; 102 char *her_appl_version; 103 uint32_t len; 104 u_char repl; 105 krb5_data data; 106 krb5_flags ap_options; 107 ssize_t n; 108 109 /* 110 * If there are no addresses in auth_context, get them from `fd'. 111 */ 112 113 if (*auth_context == NULL) { 114 ret = krb5_auth_con_init (context, auth_context); 115 if (ret) 116 return ret; 117 } 118 119 ret = krb5_auth_con_setaddrs_from_fd (context, 120 *auth_context, 121 p_fd); 122 if (ret) 123 return ret; 124 125 /* 126 * Expect SENDAUTH protocol version. 127 */ 128 if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) { 129 n = krb5_net_read (context, p_fd, &len, 4); 130 if (n < 0) { 131 ret = errno ? errno : EINVAL; 132 krb5_set_error_message(context, ret, "read: %s", strerror(ret)); 133 return ret; 134 } 135 if (n == 0) { 136 krb5_set_error_message(context, KRB5_SENDAUTH_BADAUTHVERS, 137 N_("Failed to receive sendauth data", "")); 138 return KRB5_SENDAUTH_BADAUTHVERS; 139 } 140 len = ntohl(len); 141 if (len != sizeof(her_version) 142 || krb5_net_read (context, p_fd, her_version, len) != len 143 || strncmp (version, her_version, len)) { 144 repl = 1; 145 krb5_net_write (context, p_fd, &repl, 1); 146 krb5_clear_error_message (context); 147 return KRB5_SENDAUTH_BADAUTHVERS; 148 } 149 } 150 151 /* 152 * Expect application protocol version. 153 */ 154 n = krb5_net_read (context, p_fd, &len, 4); 155 if (n < 0) { 156 ret = errno ? errno : EINVAL; 157 krb5_set_error_message(context, ret, "read: %s", strerror(ret)); 158 return ret; 159 } 160 if (n == 0) { 161 krb5_clear_error_message (context); 162 return KRB5_SENDAUTH_BADAPPLVERS; 163 } 164 len = ntohl(len); 165 her_appl_version = malloc (len); 166 if (her_appl_version == NULL) { 167 repl = 2; 168 krb5_net_write (context, p_fd, &repl, 1); 169 return krb5_enomem(context); 170 } 171 if (krb5_net_read (context, p_fd, her_appl_version, len) != len 172 || !(*match_appl_version)(match_data, her_appl_version)) { 173 repl = 2; 174 krb5_net_write (context, p_fd, &repl, 1); 175 krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS, 176 N_("wrong sendauth application version (%s)", ""), 177 her_appl_version); 178 free (her_appl_version); 179 return KRB5_SENDAUTH_BADAPPLVERS; 180 } 181 free (her_appl_version); 182 183 /* 184 * Send OK. 185 */ 186 repl = 0; 187 if (krb5_net_write (context, p_fd, &repl, 1) != 1) { 188 ret = errno ? errno : EINVAL; 189 krb5_set_error_message(context, ret, "write: %s", strerror(ret)); 190 return ret; 191 } 192 193 /* 194 * Until here, the fields in the message were in cleartext and unauthenticated. 195 * From now on, Kerberos kicks in. 196 */ 197 198 /* 199 * Expect AP_REQ. 200 */ 201 krb5_data_zero (&data); 202 ret = krb5_read_message (context, p_fd, &data); 203 if (ret) 204 return ret; 205 206 ret = krb5_rd_req (context, 207 auth_context, 208 &data, 209 server, 210 keytab, 211 &ap_options, 212 ticket); 213 krb5_data_free (&data); 214 if (ret) { 215 krb5_data error_data; 216 krb5_error_code ret2; 217 218 ret2 = krb5_mk_error (context, 219 ret, 220 NULL, 221 NULL, 222 NULL, 223 server, 224 NULL, 225 NULL, 226 &error_data); 227 if (ret2 == 0) { 228 krb5_write_message (context, p_fd, &error_data); 229 krb5_data_free (&error_data); 230 } 231 return ret; 232 } 233 234 /* 235 * Send OK. 236 */ 237 len = 0; 238 if (krb5_net_write (context, p_fd, &len, 4) != 4) { 239 ret = errno ? errno : EINVAL; 240 krb5_set_error_message(context, ret, "write: %s", strerror(ret)); 241 krb5_free_ticket(context, *ticket); 242 *ticket = NULL; 243 return ret; 244 } 245 246 /* 247 * If client requires mutual authentication, send AP_REP. 248 */ 249 if (ap_options & AP_OPTS_MUTUAL_REQUIRED) { 250 ret = krb5_mk_rep (context, *auth_context, &data); 251 if (ret) { 252 krb5_free_ticket(context, *ticket); 253 *ticket = NULL; 254 return ret; 255 } 256 257 ret = krb5_write_message (context, p_fd, &data); 258 if (ret) { 259 krb5_free_ticket(context, *ticket); 260 *ticket = NULL; 261 return ret; 262 } 263 krb5_data_free (&data); 264 } 265 return 0; 266} 267