1/* 2 * $Id: send_to_kdc.c,v 1.4 2001-06-25 20:13:45 rufustfirefly Exp $ 3 * $Author: rufustfirefly $ 4 * 5 * Copyright (c) 1990,1994 Regents of The University of Michigan. 6 * All Rights Reserved. See COPYRIGHT. 7 * 8 * $Source: /home/ralph/netatalk/rsync/netatalk/etc/uams/uams_krb4/send_to_kdc.c,v $ 9 * 10 * Copyright 1987, 1988 by the Massachusetts Institute of Technology. 11 * 12 * For copying and distribution information, please see the file 13 * <mit-copyright.h>. 14 */ 15 16#ifndef lint 17static char rcsid_send_to_kdc_c[] = 18"$Id: send_to_kdc.c,v 1.4 2001-06-25 20:13:45 rufustfirefly Exp $"; 19#endif /* lint */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif /* HAVE_CONFIG_H */ 24 25#ifdef UAM_AFSKRB 26 27#include <mit-copyright.h> 28 29#include <krb.h> 30#include <prot.h> 31 32#include <stdio.h> 33#include <string.h> 34#include <errno.h> 35#include <sys/time.h> 36#include <sys/types.h> 37#ifdef lint 38#include <sys/uio.h> /* struct iovec to make lint happy */ 39#endif /* lint */ 40#include <sys/socket.h> 41#include <netinet/in.h> 42#include <netdb.h> 43#include <string.h> 44 45#define S_AD_SZ sizeof(struct sockaddr_in) 46 47extern int krb_debug; 48 49extern char *malloc(), *calloc(), *realloc(); 50 51int krb_udp_port = 0; 52 53/* CLIENT_KRB_TIMEOUT indicates the time to wait before 54 * retrying a server. It's defined in "krb.h". 55 */ 56static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0}; 57static char *prog = "send_to_kdc"; 58static send_recv(); 59 60/* 61 * This file contains two routines, send_to_kdc() and send_recv(). 62 * send_recv() is a static routine used by send_to_kdc(). 63 */ 64 65/* 66 * send_to_kdc() sends a message to the Kerberos authentication 67 * server(s) in the given realm and returns the reply message. 68 * The "pkt" argument points to the message to be sent to Kerberos; 69 * the "rpkt" argument will be filled in with Kerberos' reply. 70 * The "realm" argument indicates the realm of the Kerberos server(s) 71 * to transact with. If the realm is null, the local realm is used. 72 * 73 * If more than one Kerberos server is known for a given realm, 74 * different servers will be queried until one of them replies. 75 * Several attempts (retries) are made for each server before 76 * giving up entirely. 77 * 78 * If an answer was received from a Kerberos host, KSUCCESS is 79 * returned. The following errors can be returned: 80 * 81 * SKDC_CANT - can't get local realm 82 * - can't find "kerberos" in /etc/services database 83 * - can't open socket 84 * - can't bind socket 85 * - all ports in use 86 * - couldn't find any Kerberos host 87 * 88 * SKDC_RETRY - couldn't get an answer from any Kerberos server, 89 * after several retries 90 */ 91 92send_to_kdc(pkt,rpkt,realm) 93 KTEXT pkt; 94 KTEXT rpkt; 95 char *realm; 96{ 97 int i, f; 98 int no_host; /* was a kerberos host found? */ 99 int retry; 100 int n_hosts; 101 int retval; 102 struct sockaddr_in to; 103 struct hostent *host, *hostlist; 104 char *cp; 105 char krbhst[MAX_HSTNM]; 106 char lrealm[REALM_SZ]; 107 108 /* 109 * If "realm" is non-null, use that, otherwise get the 110 * local realm. 111 */ 112 if (realm) 113 (void) strcpy(lrealm, realm); 114 else 115 if (krb_get_lrealm(lrealm,1)) { 116 if (krb_debug) 117 fprintf(stderr, "%s: can't get local realm\n", prog); 118 return(SKDC_CANT); 119 } 120 if (krb_debug) 121 printf("lrealm is %s\n", lrealm); 122 if (krb_udp_port == 0) { 123 register struct servent *sp; 124 if ((sp = getservbyname("kerberos","udp")) == 0) { 125 if (krb_debug) 126 fprintf(stderr, "%s: Can't get kerberos/udp service\n", 127 prog); 128 return(SKDC_CANT); 129 } 130 krb_udp_port = sp->s_port; 131 if (krb_debug) 132 printf("krb_udp_port is %d\n", krb_udp_port); 133 } 134 memset(&to, 0, S_AD_SZ); 135 hostlist = (struct hostent *) malloc(sizeof(struct hostent)); 136 if (!hostlist) 137 return (/*errno */SKDC_CANT); 138 if ((f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 139 if (krb_debug) 140 fprintf(stderr,"%s: Can't open socket\n", prog); 141 return(SKDC_CANT); 142 } 143 /* from now on, exit through rtn label for cleanup */ 144 145 no_host = 1; 146 /* get an initial allocation */ 147 n_hosts = 0; 148 for (i = 1; krb_get_krbhst(krbhst, lrealm, i) == KSUCCESS; ++i) { 149 if (krb_debug) { 150 printf("Getting host entry for %s...",krbhst); 151 (void) fflush(stdout); 152 } 153 host = gethostbyname(krbhst); 154 if (krb_debug) { 155 printf("%s.\n", 156 host ? "Got it" : "Didn't get it"); 157 (void) fflush(stdout); 158 } 159 if (!host) 160 continue; 161 no_host = 0; /* found at least one */ 162 n_hosts++; 163 /* preserve host network address to check later 164 * (would be better to preserve *all* addresses, 165 * take care of that later) 166 */ 167 hostlist = (struct hostent *) 168 realloc((char *)hostlist, 169 (unsigned) 170 sizeof(struct hostent)*(n_hosts+1)); 171 if (!hostlist) 172 return /*errno */SKDC_CANT; 173 memcpy(&hostlist[n_hosts-1], host, sizeof(struct hostent)); 174 host = &hostlist[n_hosts-1]; 175 cp = malloc((unsigned)host->h_length); 176 if (!cp) { 177 retval = /*errno */SKDC_CANT; 178 goto rtn; 179 } 180 memcpy(cp, host->h_addr, host->h_length); 181/* At least Sun OS version 3.2 (or worse) and Ultrix version 2.2 182 (or worse) only return one name ... */ 183#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40)) 184 host->h_addr_list = (char **)malloc(sizeof(char *)); 185 if (!host->h_addr_list) { 186 retval = /*errno */SKDC_CANT; 187 goto rtn; 188 } 189#endif /* ULTRIX022 || SunOS */ 190 host->h_addr = cp; 191 memset(&hostlist[n_hosts], 0, sizeof(struct hostent)); 192 to.sin_family = host->h_addrtype; 193 memcpy(&to.sin_addr, host->h_addr, host->h_length); 194 to.sin_port = krb_udp_port; 195 if (send_recv(pkt, rpkt, f, &to, hostlist)) { 196 retval = KSUCCESS; 197 goto rtn; 198 } 199 if (krb_debug) { 200 printf("Timeout, error, or wrong descriptor\n"); 201 (void) fflush(stdout); 202 } 203 } 204 if (no_host) { 205 if (krb_debug) 206 fprintf(stderr, "%s: can't find any Kerberos host.\n", 207 prog); 208 retval = SKDC_CANT; 209 goto rtn; 210 } 211 /* retry each host in sequence */ 212 for (retry = 0; retry < CLIENT_KRB_RETRY; ++retry) { 213 for (host = hostlist; host->h_name != (char *)NULL; host++) { 214 to.sin_family = host->h_addrtype; 215 memcpy(&to.sin_addr, host->h_addr, host->h_length); 216 if (send_recv(pkt, rpkt, f, &to, hostlist)) { 217 retval = KSUCCESS; 218 goto rtn; 219 } 220 } 221 } 222 retval = SKDC_RETRY; 223rtn: 224 (void) close(f); 225 if (hostlist) { 226 register struct hostent *hp; 227 for (hp = hostlist; hp->h_name; hp++) 228#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40)) 229 if (hp->h_addr_list) { 230#endif /* ULTRIX022 || SunOS */ 231 if (hp->h_addr) 232 free(hp->h_addr); 233#if !(defined(ULTRIX022) || (defined(SunOS) && SunOS < 40)) 234 free((char *)hp->h_addr_list); 235 } 236#endif /* ULTRIX022 || SunOS */ 237 free((char *)hostlist); 238 } 239 return(retval); 240} 241 242/* 243 * try to send out and receive message. 244 * return 1 on success, 0 on failure 245 */ 246 247static send_recv(pkt,rpkt,f,_to,addrs) 248 KTEXT pkt; 249 KTEXT rpkt; 250 int f; 251 struct sockaddr_in *_to; 252 struct hostent *addrs; 253{ 254 fd_set readfds; 255 register struct hostent *hp; 256 struct sockaddr_in from; 257 int sin_size, rc; 258 int numsent; 259 260 if (krb_debug) { 261 if (_to->sin_family == AF_INET) 262 printf("Sending message to %s...", 263 inet_ntoa(_to->sin_addr)); 264 else 265 printf("Sending message..."); 266 (void) fflush(stdout); 267 } 268 if ((numsent = sendto(f,(char *)(pkt->dat), pkt->length, 0, 269 (struct sockaddr *)_to, 270 S_AD_SZ)) != pkt->length) { 271 if (krb_debug) 272 printf("sent only %d/%d\n",numsent, pkt->length); 273 return 0; 274 } 275 if (krb_debug) { 276 printf("Sent\nWaiting for reply..."); 277 (void) fflush(stdout); 278 } 279 FD_ZERO(&readfds); 280 FD_SET(f, &readfds); 281 errno = 0; 282 /* select - either recv is ready, or timeout */ 283 /* see if timeout or error or wrong descriptor */ 284 if (select(f + 1, &readfds, (fd_set *)0, (fd_set *)0, &timeout) < 1 285 || !FD_ISSET(f, &readfds)) { 286 if (krb_debug) { 287 fprintf(stderr, "select failed: readfds=%x", 288 readfds); 289 perror(""); 290 } 291 return 0; 292 } 293 sin_size = sizeof(from); 294 if (( rc = recvfrom(f, (char *)(rpkt->dat), sizeof(rpkt->dat), 0, 295 (struct sockaddr *)&from, &sin_size)) < 0) { 296 if (krb_debug) 297 perror("recvfrom"); 298 return 0; 299 } 300 rpkt->length = rc; 301 if (krb_debug) { 302 printf("received packet from %s\n", inet_ntoa(from.sin_addr)); 303 fflush(stdout); 304 } 305 for (hp = addrs; hp->h_name != (char *)NULL; hp++) { 306 if (!memcmp(hp->h_addr, (char *)&from.sin_addr.s_addr, 307 hp->h_length)) { 308 if (krb_debug) { 309 printf("Received it\n"); 310 (void) fflush(stdout); 311 } 312 return 1; 313 } 314 if (krb_debug) 315 fprintf(stderr, 316 "packet not from %x\n", 317 hp->h_addr); 318 } 319 if (krb_debug) 320 fprintf(stderr, "%s: received packet from wrong host! (%x)\n", 321 "send_to_kdc(send_rcv)", from.sin_addr.s_addr); 322 return 0; 323} 324 325#endif /* UAM_AFSKRB */ 326