1 /* 2 * Copyright (c) 1995 - 1999, 2003 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "ftpd_locl.h" 35 36 RCSID("$Id: kauth.c 15666 2005-07-19 17:08:11Z lha $"); 37 38 #if defined(KRB4) || defined(KRB5) 39 40 int do_destroy_tickets = 1; 41 char *k5ccname; 42 43 #endif 44 45 #ifdef KRB4 46 47 static KTEXT_ST cip; 48 static unsigned int lifetime; 49 static time_t local_time; 50 51 static krb_principal pr; 52 53 static int 54 save_tkt(const char *user, 55 const char *instance, 56 const char *realm, 57 const void *arg, 58 key_proc_t key_proc, 59 KTEXT *cipp) 60 { 61 local_time = time(0); 62 memmove(&cip, *cipp, sizeof(cip)); 63 return -1; 64 } 65 66 static int 67 store_ticket(KTEXT cip) 68 { 69 char *ptr; 70 des_cblock session; 71 krb_principal sp; 72 unsigned char kvno; 73 KTEXT_ST tkt; 74 int left = cip->length; 75 int len; 76 int kerror; 77 78 ptr = (char *) cip->dat; 79 80 /* extract session key */ 81 memmove(session, ptr, 8); 82 ptr += 8; 83 left -= 8; 84 85 len = strnlen(ptr, left); 86 if (len == left) 87 return(INTK_BADPW); 88 89 /* extract server's name */ 90 strlcpy(sp.name, ptr, sizeof(sp.name)); 91 ptr += len + 1; 92 left -= len + 1; 93 94 len = strnlen(ptr, left); 95 if (len == left) 96 return(INTK_BADPW); 97 98 /* extract server's instance */ 99 strlcpy(sp.instance, ptr, sizeof(sp.instance)); 100 ptr += len + 1; 101 left -= len + 1; 102 103 len = strnlen(ptr, left); 104 if (len == left) 105 return(INTK_BADPW); 106 107 /* extract server's realm */ 108 strlcpy(sp.realm, ptr, sizeof(sp.realm)); 109 ptr += len + 1; 110 left -= len + 1; 111 112 if(left < 3) 113 return INTK_BADPW; 114 /* extract ticket lifetime, server key version, ticket length */ 115 /* be sure to avoid sign extension on lifetime! */ 116 lifetime = (unsigned char) ptr[0]; 117 kvno = (unsigned char) ptr[1]; 118 tkt.length = (unsigned char) ptr[2]; 119 ptr += 3; 120 left -= 3; 121 122 if (tkt.length > left) 123 return(INTK_BADPW); 124 125 /* extract ticket itself */ 126 memmove(tkt.dat, ptr, tkt.length); 127 ptr += tkt.length; 128 left -= tkt.length; 129 130 /* Here is where the time should be verified against the KDC. 131 * Unfortunately everything is sent in host byte order (receiver 132 * makes wrong) , and at this stage there is no way for us to know 133 * which byteorder the KDC has. So we simply ignore the time, 134 * there are no security risks with this, the only thing that can 135 * happen is that we might receive a replayed ticket, which could 136 * at most be useless. 137 */ 138 139 #if 0 140 /* check KDC time stamp */ 141 { 142 time_t kdc_time; 143 144 memmove(&kdc_time, ptr, sizeof(kdc_time)); 145 if (swap_bytes) swap_u_long(kdc_time); 146 147 ptr += 4; 148 149 if (abs((int)(local_time - kdc_time)) > CLOCK_SKEW) { 150 return(RD_AP_TIME); /* XXX should probably be better 151 code */ 152 } 153 } 154 #endif 155 156 /* initialize ticket cache */ 157 158 if (tf_create(TKT_FILE) != KSUCCESS) 159 return(INTK_ERR); 160 161 if (tf_put_pname(pr.name) != KSUCCESS || 162 tf_put_pinst(pr.instance) != KSUCCESS) { 163 tf_close(); 164 return(INTK_ERR); 165 } 166 167 168 kerror = tf_save_cred(sp.name, sp.instance, sp.realm, session, 169 lifetime, kvno, &tkt, local_time); 170 tf_close(); 171 172 return(kerror); 173 } 174 175 void 176 kauth(char *principal, char *ticket) 177 { 178 char *p; 179 int ret; 180 181 if(get_command_prot() != prot_private) { 182 reply(500, "Request denied (bad protection level)"); 183 return; 184 } 185 ret = krb_parse_name(principal, &pr); 186 if(ret){ 187 reply(500, "Bad principal: %s.", krb_get_err_text(ret)); 188 return; 189 } 190 if(pr.realm[0] == 0) 191 krb_get_lrealm(pr.realm, 1); 192 193 if(ticket){ 194 cip.length = base64_decode(ticket, &cip.dat); 195 if(cip.length == -1){ 196 reply(500, "Failed to decode data."); 197 return; 198 } 199 ret = store_ticket(&cip); 200 if(ret){ 201 reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); 202 memset(&cip, 0, sizeof(cip)); 203 return; 204 } 205 do_destroy_tickets = 1; 206 207 if(k_hasafs()) 208 krb_afslog(0, 0); 209 reply(200, "Tickets will be destroyed on exit."); 210 return; 211 } 212 213 ret = krb_get_in_tkt (pr.name, 214 pr.instance, 215 pr.realm, 216 KRB_TICKET_GRANTING_TICKET, 217 pr.realm, 218 DEFAULT_TKT_LIFE, 219 NULL, save_tkt, NULL); 220 if(ret != INTK_BADPW){ 221 reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); 222 return; 223 } 224 if(base64_encode(cip.dat, cip.length, &p) < 0) { 225 reply(500, "Out of memory while base64-encoding."); 226 return; 227 } 228 reply(300, "P=%s T=%s", krb_unparse_name(&pr), p); 229 free(p); 230 memset(&cip, 0, sizeof(cip)); 231 } 232 233 234 static char * 235 short_date(int32_t dp) 236 { 237 char *cp; 238 time_t t = (time_t)dp; 239 240 if (t == (time_t)(-1L)) return "*** Never *** "; 241 cp = ctime(&t) + 4; 242 cp[15] = '\0'; 243 return (cp); 244 } 245 246 void 247 krbtkfile(const char *tkfile) 248 { 249 do_destroy_tickets = 0; 250 krb_set_tkt_string(tkfile); 251 reply(200, "Using ticket file %s", tkfile); 252 } 253 254 #endif /* KRB4 */ 255 256 #ifdef KRB5 257 258 static void 259 dest_cc(void) 260 { 261 krb5_context context; 262 krb5_error_code ret; 263 krb5_ccache id; 264 265 ret = krb5_init_context(&context); 266 if (ret == 0) { 267 if (k5ccname) 268 ret = krb5_cc_resolve(context, k5ccname, &id); 269 else 270 ret = krb5_cc_default (context, &id); 271 if (ret) 272 krb5_free_context(context); 273 } 274 if (ret == 0) { 275 krb5_cc_destroy(context, id); 276 krb5_free_context (context); 277 } 278 } 279 #endif 280 281 #if defined(KRB4) || defined(KRB5) 282 283 /* 284 * Only destroy if we created the tickets 285 */ 286 287 void 288 cond_kdestroy(void) 289 { 290 if (do_destroy_tickets) { 291 #if KRB4 292 dest_tkt(); 293 #endif 294 #if KRB5 295 dest_cc(); 296 #endif 297 do_destroy_tickets = 0; 298 } 299 afsunlog(); 300 } 301 302 void 303 kdestroy(void) 304 { 305 #if KRB4 306 dest_tkt(); 307 #endif 308 #if KRB5 309 dest_cc(); 310 #endif 311 afsunlog(); 312 reply(200, "Tickets destroyed"); 313 } 314 315 316 void 317 afslog(const char *cell, int quiet) 318 { 319 if(k_hasafs()) { 320 #ifdef KRB5 321 krb5_context context; 322 krb5_error_code ret; 323 krb5_ccache id; 324 325 ret = krb5_init_context(&context); 326 if (ret == 0) { 327 if (k5ccname) 328 ret = krb5_cc_resolve(context, k5ccname, &id); 329 else 330 ret = krb5_cc_default(context, &id); 331 if (ret) 332 krb5_free_context(context); 333 } 334 if (ret == 0) { 335 krb5_afslog(context, id, cell, 0); 336 krb5_cc_close (context, id); 337 krb5_free_context (context); 338 } 339 #endif 340 #ifdef KRB4 341 krb_afslog(cell, 0); 342 #endif 343 if (!quiet) 344 reply(200, "afslog done"); 345 } else { 346 if (!quiet) 347 reply(200, "no AFS present"); 348 } 349 } 350 351 void 352 afsunlog(void) 353 { 354 if(k_hasafs()) 355 k_unlog(); 356 } 357 358 #else 359 int ftpd_afslog_placeholder; 360 #endif /* KRB4 || KRB5 */ 361