1 /* 2 * Copyright (c) 1995, 1996, 1997, 1998 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,v 1.25 1999/12/02 16:58:31 joda Exp $"); 37 38 static KTEXT_ST cip; 39 static unsigned int lifetime; 40 static time_t local_time; 41 42 static krb_principal pr; 43 44 static int do_destroy_tickets = 1; 45 46 static int 47 save_tkt(const char *user, 48 const char *instance, 49 const char *realm, 50 const void *arg, 51 key_proc_t key_proc, 52 KTEXT *cipp) 53 { 54 local_time = time(0); 55 memmove(&cip, *cipp, sizeof(cip)); 56 return -1; 57 } 58 59 static int 60 store_ticket(KTEXT cip) 61 { 62 char *ptr; 63 des_cblock session; 64 krb_principal sp; 65 unsigned char kvno; 66 KTEXT_ST tkt; 67 int left = cip->length; 68 int len; 69 int kerror; 70 71 ptr = (char *) cip->dat; 72 73 /* extract session key */ 74 memmove(session, ptr, 8); 75 ptr += 8; 76 left -= 8; 77 78 len = strnlen(ptr, left); 79 if (len == left) 80 return(INTK_BADPW); 81 82 /* extract server's name */ 83 strlcpy(sp.name, ptr, sizeof(sp.name)); 84 ptr += len + 1; 85 left -= len + 1; 86 87 len = strnlen(ptr, left); 88 if (len == left) 89 return(INTK_BADPW); 90 91 /* extract server's instance */ 92 strlcpy(sp.instance, ptr, sizeof(sp.instance)); 93 ptr += len + 1; 94 left -= len + 1; 95 96 len = strnlen(ptr, left); 97 if (len == left) 98 return(INTK_BADPW); 99 100 /* extract server's realm */ 101 strlcpy(sp.realm, ptr, sizeof(sp.realm)); 102 ptr += len + 1; 103 left -= len + 1; 104 105 if(left < 3) 106 return INTK_BADPW; 107 /* extract ticket lifetime, server key version, ticket length */ 108 /* be sure to avoid sign extension on lifetime! */ 109 lifetime = (unsigned char) ptr[0]; 110 kvno = (unsigned char) ptr[1]; 111 tkt.length = (unsigned char) ptr[2]; 112 ptr += 3; 113 left -= 3; 114 115 if (tkt.length > left) 116 return(INTK_BADPW); 117 118 /* extract ticket itself */ 119 memmove(tkt.dat, ptr, tkt.length); 120 ptr += tkt.length; 121 left -= tkt.length; 122 123 /* Here is where the time should be verified against the KDC. 124 * Unfortunately everything is sent in host byte order (receiver 125 * makes wrong) , and at this stage there is no way for us to know 126 * which byteorder the KDC has. So we simply ignore the time, 127 * there are no security risks with this, the only thing that can 128 * happen is that we might receive a replayed ticket, which could 129 * at most be useless. 130 */ 131 132 #if 0 133 /* check KDC time stamp */ 134 { 135 time_t kdc_time; 136 137 memmove(&kdc_time, ptr, sizeof(kdc_time)); 138 if (swap_bytes) swap_u_long(kdc_time); 139 140 ptr += 4; 141 142 if (abs((int)(local_time - kdc_time)) > CLOCK_SKEW) { 143 return(RD_AP_TIME); /* XXX should probably be better 144 code */ 145 } 146 } 147 #endif 148 149 /* initialize ticket cache */ 150 151 if (tf_create(TKT_FILE) != KSUCCESS) 152 return(INTK_ERR); 153 154 if (tf_put_pname(pr.name) != KSUCCESS || 155 tf_put_pinst(pr.instance) != KSUCCESS) { 156 tf_close(); 157 return(INTK_ERR); 158 } 159 160 161 kerror = tf_save_cred(sp.name, sp.instance, sp.realm, session, 162 lifetime, kvno, &tkt, local_time); 163 tf_close(); 164 165 return(kerror); 166 } 167 168 void 169 kauth(char *principal, char *ticket) 170 { 171 char *p; 172 int ret; 173 174 if(get_command_prot() != prot_private) { 175 reply(500, "Request denied (bad protection level)"); 176 return; 177 } 178 ret = krb_parse_name(principal, &pr); 179 if(ret){ 180 reply(500, "Bad principal: %s.", krb_get_err_text(ret)); 181 return; 182 } 183 if(pr.realm[0] == 0) 184 krb_get_lrealm(pr.realm, 1); 185 186 if(ticket){ 187 cip.length = base64_decode(ticket, &cip.dat); 188 if(cip.length == -1){ 189 reply(500, "Failed to decode data."); 190 return; 191 } 192 ret = store_ticket(&cip); 193 if(ret){ 194 reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); 195 memset(&cip, 0, sizeof(cip)); 196 return; 197 } 198 do_destroy_tickets = 1; 199 200 if(k_hasafs()) 201 krb_afslog(0, 0); 202 reply(200, "Tickets will be destroyed on exit."); 203 return; 204 } 205 206 ret = krb_get_in_tkt (pr.name, 207 pr.instance, 208 pr.realm, 209 KRB_TICKET_GRANTING_TICKET, 210 pr.realm, 211 DEFAULT_TKT_LIFE, 212 NULL, save_tkt, NULL); 213 if(ret != INTK_BADPW){ 214 reply(500, "Kerberos error: %s.", krb_get_err_text(ret)); 215 return; 216 } 217 if(base64_encode(cip.dat, cip.length, &p) < 0) { 218 reply(500, "Out of memory while base64-encoding."); 219 return; 220 } 221 reply(300, "P=%s T=%s", krb_unparse_name(&pr), p); 222 free(p); 223 memset(&cip, 0, sizeof(cip)); 224 } 225 226 227 static char * 228 short_date(int32_t dp) 229 { 230 char *cp; 231 time_t t = (time_t)dp; 232 233 if (t == (time_t)(-1L)) return "*** Never *** "; 234 cp = ctime(&t) + 4; 235 cp[15] = '\0'; 236 return (cp); 237 } 238 239 void 240 klist(void) 241 { 242 int err; 243 244 char *file = tkt_string(); 245 246 krb_principal pr; 247 248 char buf1[128], buf2[128]; 249 int header = 1; 250 CREDENTIALS c; 251 252 253 254 err = tf_init(file, R_TKT_FIL); 255 if(err != KSUCCESS){ 256 reply(500, "%s", krb_get_err_text(err)); 257 return; 258 } 259 tf_close(); 260 261 /* 262 * We must find the realm of the ticket file here before calling 263 * tf_init because since the realm of the ticket file is not 264 * really stored in the principal section of the file, the 265 * routine we use must itself call tf_init and tf_close. 266 */ 267 err = krb_get_tf_realm(file, pr.realm); 268 if(err != KSUCCESS){ 269 reply(500, "%s", krb_get_err_text(err)); 270 return; 271 } 272 273 err = tf_init(file, R_TKT_FIL); 274 if(err != KSUCCESS){ 275 reply(500, "%s", krb_get_err_text(err)); 276 return; 277 } 278 279 err = tf_get_pname(pr.name); 280 if(err != KSUCCESS){ 281 reply(500, "%s", krb_get_err_text(err)); 282 return; 283 } 284 err = tf_get_pinst(pr.instance); 285 if(err != KSUCCESS){ 286 reply(500, "%s", krb_get_err_text(err)); 287 return; 288 } 289 290 /* 291 * You may think that this is the obvious place to get the 292 * realm of the ticket file, but it can't be done here as the 293 * routine to do this must open the ticket file. This is why 294 * it was done before tf_init. 295 */ 296 297 lreply(200, "Ticket file: %s", tkt_string()); 298 299 lreply(200, "Principal: %s", krb_unparse_name(&pr)); 300 while ((err = tf_get_cred(&c)) == KSUCCESS) { 301 if (header) { 302 lreply(200, "%-15s %-15s %s", 303 " Issued", " Expires", " Principal (kvno)"); 304 header = 0; 305 } 306 strlcpy(buf1, short_date(c.issue_date), sizeof(buf1)); 307 c.issue_date = krb_life_to_time(c.issue_date, c.lifetime); 308 if (time(0) < (unsigned long) c.issue_date) 309 strlcpy(buf2, short_date(c.issue_date), sizeof(buf2)); 310 else 311 strlcpy(buf2, ">>> Expired <<< ", sizeof(buf2)); 312 lreply(200, "%s %s %s (%d)", buf1, buf2, 313 krb_unparse_name_long(c.service, c.instance, c.realm), c.kvno); 314 } 315 if (header && err == EOF) { 316 lreply(200, "No tickets in file."); 317 } 318 reply(200, " "); 319 } 320 321 /* 322 * Only destroy if we created the tickets 323 */ 324 325 void 326 cond_kdestroy(void) 327 { 328 if (do_destroy_tickets) 329 dest_tkt(); 330 afsunlog(); 331 } 332 333 void 334 kdestroy(void) 335 { 336 dest_tkt(); 337 afsunlog(); 338 reply(200, "Tickets destroyed"); 339 } 340 341 void 342 krbtkfile(const char *tkfile) 343 { 344 do_destroy_tickets = 0; 345 krb_set_tkt_string(tkfile); 346 reply(200, "Using ticket file %s", tkfile); 347 } 348 349 void 350 afslog(const char *cell) 351 { 352 if(k_hasafs()) { 353 krb_afslog(cell, 0); 354 reply(200, "afslog done"); 355 } else { 356 reply(200, "no AFS present"); 357 } 358 } 359 360 void 361 afsunlog(void) 362 { 363 if(k_hasafs()) 364 k_unlog(); 365 } 366