1 1.1 elric /* $NetBSD: recvauth.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 1.1 elric 3 1.1 elric /* 4 1.1 elric * Copyright (c) 1997-2007 Kungliga Tekniska Hgskolan 5 1.1 elric * (Royal Institute of Technology, Stockholm, Sweden). 6 1.1 elric * All rights reserved. 7 1.1 elric * 8 1.1 elric * Redistribution and use in source and binary forms, with or without 9 1.1 elric * modification, are permitted provided that the following conditions 10 1.1 elric * are met: 11 1.1 elric * 12 1.1 elric * 1. Redistributions of source code must retain the above copyright 13 1.1 elric * notice, this list of conditions and the following disclaimer. 14 1.1 elric * 15 1.1 elric * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 elric * notice, this list of conditions and the following disclaimer in the 17 1.1 elric * documentation and/or other materials provided with the distribution. 18 1.1 elric * 19 1.1 elric * 3. Neither the name of the Institute nor the names of its contributors 20 1.1 elric * may be used to endorse or promote products derived from this software 21 1.1 elric * without specific prior written permission. 22 1.1 elric * 23 1.1 elric * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 1.1 elric * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 1.1 elric * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 1.1 elric * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 1.1 elric * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 1.1 elric * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 1.1 elric * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 1.1 elric * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 1.1 elric * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 1.1 elric * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 1.1 elric * SUCH DAMAGE. 34 1.1 elric */ 35 1.1 elric 36 1.1 elric #include "krb5_locl.h" 37 1.1 elric 38 1.1 elric /* 39 1.1 elric * See `sendauth.c' for the format. 40 1.1 elric */ 41 1.1 elric 42 1.1 elric static krb5_boolean 43 1.1 elric match_exact(const void *data, const char *appl_version) 44 1.1 elric { 45 1.1 elric return strcmp(data, appl_version) == 0; 46 1.1 elric } 47 1.1 elric 48 1.2 christos /** 49 1.2 christos * Perform the server side of the sendauth protocol. 50 1.2 christos * 51 1.2 christos * @param context Kerberos 5 context. 52 1.2 christos * @param auth_context authentication context of the peer. 53 1.2 christos * @param p_fd socket associated to the connection. 54 1.2 christos * @param appl_version server-specific string. 55 1.2 christos * @param server server principal. 56 1.2 christos * @param flags if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version 57 1.2 christos * part of the protocol. 58 1.2 christos * @param keytab server keytab. 59 1.2 christos * @param ticket on success, set to the authenticated client credentials. 60 1.2 christos * Must be deallocated with krb5_free_ticket(). If not 61 1.2 christos * interested, pass a NULL value. 62 1.2 christos * 63 1.2 christos * @return 0 to indicate success. Otherwise a Kerberos error code is 64 1.2 christos * returned, see krb5_get_error_message(). 65 1.2 christos */ 66 1.1 elric KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 67 1.1 elric krb5_recvauth(krb5_context context, 68 1.1 elric krb5_auth_context *auth_context, 69 1.1 elric krb5_pointer p_fd, 70 1.1 elric const char *appl_version, 71 1.1 elric krb5_principal server, 72 1.1 elric int32_t flags, 73 1.1 elric krb5_keytab keytab, 74 1.1 elric krb5_ticket **ticket) 75 1.1 elric { 76 1.1 elric return krb5_recvauth_match_version(context, auth_context, p_fd, 77 1.1 elric match_exact, appl_version, 78 1.1 elric server, flags, 79 1.1 elric keytab, ticket); 80 1.1 elric } 81 1.1 elric 82 1.2 christos /** 83 1.2 christos * Perform the server side of the sendauth protocol like krb5_recvauth(), but support 84 1.2 christos * a user-specified callback, \a match_appl_version, to perform the match of the application 85 1.2 christos * version \a match_data. 86 1.2 christos */ 87 1.1 elric KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 88 1.1 elric krb5_recvauth_match_version(krb5_context context, 89 1.1 elric krb5_auth_context *auth_context, 90 1.1 elric krb5_pointer p_fd, 91 1.1 elric krb5_boolean (*match_appl_version)(const void *, 92 1.1 elric const char*), 93 1.1 elric const void *match_data, 94 1.1 elric krb5_principal server, 95 1.1 elric int32_t flags, 96 1.1 elric krb5_keytab keytab, 97 1.1 elric krb5_ticket **ticket) 98 1.1 elric { 99 1.1 elric krb5_error_code ret; 100 1.1 elric const char *version = KRB5_SENDAUTH_VERSION; 101 1.1 elric char her_version[sizeof(KRB5_SENDAUTH_VERSION)]; 102 1.1 elric char *her_appl_version; 103 1.1 elric uint32_t len; 104 1.1 elric u_char repl; 105 1.1 elric krb5_data data; 106 1.1 elric krb5_flags ap_options; 107 1.1 elric ssize_t n; 108 1.1 elric 109 1.1 elric /* 110 1.1 elric * If there are no addresses in auth_context, get them from `fd'. 111 1.1 elric */ 112 1.1 elric 113 1.1 elric if (*auth_context == NULL) { 114 1.1 elric ret = krb5_auth_con_init (context, auth_context); 115 1.1 elric if (ret) 116 1.1 elric return ret; 117 1.1 elric } 118 1.1 elric 119 1.1 elric ret = krb5_auth_con_setaddrs_from_fd (context, 120 1.1 elric *auth_context, 121 1.1 elric p_fd); 122 1.1 elric if (ret) 123 1.1 elric return ret; 124 1.1 elric 125 1.2 christos /* 126 1.2 christos * Expect SENDAUTH protocol version. 127 1.2 christos */ 128 1.1 elric if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) { 129 1.1 elric n = krb5_net_read (context, p_fd, &len, 4); 130 1.1 elric if (n < 0) { 131 1.2 christos ret = errno ? errno : EINVAL; 132 1.1 elric krb5_set_error_message(context, ret, "read: %s", strerror(ret)); 133 1.1 elric return ret; 134 1.1 elric } 135 1.1 elric if (n == 0) { 136 1.1 elric krb5_set_error_message(context, KRB5_SENDAUTH_BADAUTHVERS, 137 1.1 elric N_("Failed to receive sendauth data", "")); 138 1.1 elric return KRB5_SENDAUTH_BADAUTHVERS; 139 1.1 elric } 140 1.1 elric len = ntohl(len); 141 1.1 elric if (len != sizeof(her_version) 142 1.1 elric || krb5_net_read (context, p_fd, her_version, len) != len 143 1.1 elric || strncmp (version, her_version, len)) { 144 1.1 elric repl = 1; 145 1.1 elric krb5_net_write (context, p_fd, &repl, 1); 146 1.1 elric krb5_clear_error_message (context); 147 1.1 elric return KRB5_SENDAUTH_BADAUTHVERS; 148 1.1 elric } 149 1.1 elric } 150 1.1 elric 151 1.2 christos /* 152 1.2 christos * Expect application protocol version. 153 1.2 christos */ 154 1.1 elric n = krb5_net_read (context, p_fd, &len, 4); 155 1.1 elric if (n < 0) { 156 1.2 christos ret = errno ? errno : EINVAL; 157 1.1 elric krb5_set_error_message(context, ret, "read: %s", strerror(ret)); 158 1.1 elric return ret; 159 1.1 elric } 160 1.1 elric if (n == 0) { 161 1.1 elric krb5_clear_error_message (context); 162 1.1 elric return KRB5_SENDAUTH_BADAPPLVERS; 163 1.1 elric } 164 1.1 elric len = ntohl(len); 165 1.1 elric her_appl_version = malloc (len); 166 1.1 elric if (her_appl_version == NULL) { 167 1.1 elric repl = 2; 168 1.1 elric krb5_net_write (context, p_fd, &repl, 1); 169 1.2 christos return krb5_enomem(context); 170 1.1 elric } 171 1.1 elric if (krb5_net_read (context, p_fd, her_appl_version, len) != len 172 1.1 elric || !(*match_appl_version)(match_data, her_appl_version)) { 173 1.1 elric repl = 2; 174 1.1 elric krb5_net_write (context, p_fd, &repl, 1); 175 1.1 elric krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS, 176 1.2 christos N_("wrong sendauth application version (%s)", ""), 177 1.1 elric her_appl_version); 178 1.1 elric free (her_appl_version); 179 1.1 elric return KRB5_SENDAUTH_BADAPPLVERS; 180 1.1 elric } 181 1.1 elric free (her_appl_version); 182 1.1 elric 183 1.2 christos /* 184 1.2 christos * Send OK. 185 1.2 christos */ 186 1.1 elric repl = 0; 187 1.1 elric if (krb5_net_write (context, p_fd, &repl, 1) != 1) { 188 1.2 christos ret = errno ? errno : EINVAL; 189 1.1 elric krb5_set_error_message(context, ret, "write: %s", strerror(ret)); 190 1.1 elric return ret; 191 1.1 elric } 192 1.1 elric 193 1.2 christos /* 194 1.2 christos * Until here, the fields in the message were in cleartext and unauthenticated. 195 1.2 christos * From now on, Kerberos kicks in. 196 1.2 christos */ 197 1.2 christos 198 1.2 christos /* 199 1.2 christos * Expect AP_REQ. 200 1.2 christos */ 201 1.1 elric krb5_data_zero (&data); 202 1.1 elric ret = krb5_read_message (context, p_fd, &data); 203 1.1 elric if (ret) 204 1.1 elric return ret; 205 1.1 elric 206 1.1 elric ret = krb5_rd_req (context, 207 1.1 elric auth_context, 208 1.1 elric &data, 209 1.1 elric server, 210 1.1 elric keytab, 211 1.1 elric &ap_options, 212 1.1 elric ticket); 213 1.1 elric krb5_data_free (&data); 214 1.1 elric if (ret) { 215 1.1 elric krb5_data error_data; 216 1.1 elric krb5_error_code ret2; 217 1.1 elric 218 1.1 elric ret2 = krb5_mk_error (context, 219 1.1 elric ret, 220 1.1 elric NULL, 221 1.1 elric NULL, 222 1.1 elric NULL, 223 1.1 elric server, 224 1.1 elric NULL, 225 1.1 elric NULL, 226 1.1 elric &error_data); 227 1.1 elric if (ret2 == 0) { 228 1.1 elric krb5_write_message (context, p_fd, &error_data); 229 1.1 elric krb5_data_free (&error_data); 230 1.1 elric } 231 1.1 elric return ret; 232 1.1 elric } 233 1.1 elric 234 1.2 christos /* 235 1.2 christos * Send OK. 236 1.2 christos */ 237 1.1 elric len = 0; 238 1.1 elric if (krb5_net_write (context, p_fd, &len, 4) != 4) { 239 1.2 christos ret = errno ? errno : EINVAL; 240 1.1 elric krb5_set_error_message(context, ret, "write: %s", strerror(ret)); 241 1.1 elric krb5_free_ticket(context, *ticket); 242 1.1 elric *ticket = NULL; 243 1.1 elric return ret; 244 1.1 elric } 245 1.1 elric 246 1.2 christos /* 247 1.2 christos * If client requires mutual authentication, send AP_REP. 248 1.2 christos */ 249 1.1 elric if (ap_options & AP_OPTS_MUTUAL_REQUIRED) { 250 1.1 elric ret = krb5_mk_rep (context, *auth_context, &data); 251 1.1 elric if (ret) { 252 1.1 elric krb5_free_ticket(context, *ticket); 253 1.1 elric *ticket = NULL; 254 1.1 elric return ret; 255 1.1 elric } 256 1.1 elric 257 1.1 elric ret = krb5_write_message (context, p_fd, &data); 258 1.1 elric if (ret) { 259 1.1 elric krb5_free_ticket(context, *ticket); 260 1.1 elric *ticket = NULL; 261 1.1 elric return ret; 262 1.1 elric } 263 1.1 elric krb5_data_free (&data); 264 1.1 elric } 265 1.1 elric return 0; 266 1.1 elric } 267