1 /* 2 * Copyright (c) 1997 - 2000 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 "kf_locl.h" 35 RCSID("$Id: kf.c,v 1.15 2001/02/20 01:44:44 assar Exp $"); 36 37 krb5_context context; 38 static int help_flag; 39 static int version_flag; 40 static char *port_str; 41 const char *service = SERVICE; 42 const char *remote_name = NULL; 43 int forwardable = 0; 44 const char *ccache_name = NULL; 45 46 static struct getargs args[] = { 47 { "port", 'p', arg_string, &port_str, "port to connect to", "port" }, 48 { "login", 'l',arg_string, &remote_name,"remote login name","login"}, 49 { "ccache", 'c',arg_string, &ccache_name, "remote cred cache","ccache"}, 50 { "forwardable",'F',arg_flag,&forwardable, 51 "Forward forwardable credentials", NULL }, 52 { "forwardable",'G',arg_negative_flag,&forwardable, 53 "Don't forward forwardable credentials", NULL }, 54 { "help", 'h', arg_flag, &help_flag }, 55 { "version", 0, arg_flag, &version_flag } 56 }; 57 58 static int num_args = sizeof(args) / sizeof(args[0]); 59 60 static void 61 usage(int code, struct getargs *args, int num_args) 62 { 63 arg_printusage(args, num_args, NULL, "hosts"); 64 exit(code); 65 } 66 67 static int 68 client_setup(krb5_context *context, int *argc, char **argv) 69 { 70 int optind = 0; 71 int port = 0; 72 int status; 73 74 setprogname (argv[0]); 75 76 status = krb5_init_context (context); 77 if (status) 78 errx(1, "krb5_init_context failed: %d", status); 79 80 forwardable = krb5_config_get_bool (*context, NULL, 81 "libdefaults", 82 "forwardable", 83 NULL); 84 85 if (getarg (args, num_args, *argc, argv, &optind)) 86 usage(1, args, num_args); 87 88 if(help_flag) 89 usage (0, args, num_args); 90 if(version_flag) { 91 print_version(NULL); 92 exit(0); 93 } 94 95 if(port_str) { 96 struct servent *s = roken_getservbyname(port_str, "tcp"); 97 if(s) 98 port = s->s_port; 99 else { 100 char *ptr; 101 102 port = strtol (port_str, &ptr, 10); 103 if (port == 0 && ptr == port_str) 104 errx (1, "Bad port `%s'", port_str); 105 port = htons(port); 106 } 107 } 108 109 if (port == 0) 110 port = krb5_getportbyname (*context, PORT, "tcp", PORT_NUM); 111 112 if(*argc - optind < 1) 113 usage(1, args, num_args); 114 *argc = optind; 115 116 return port; 117 } 118 119 /* 120 * forward creds to `hostname'/`service' over `sock' 121 * return 0 iff OK 122 */ 123 124 static int 125 proto (int sock, const char *hostname, const char *service) 126 { 127 krb5_auth_context auth_context; 128 krb5_error_code status; 129 krb5_principal server; 130 krb5_data data; 131 krb5_data packet; 132 krb5_data data_send; 133 u_int32_t len, net_len; 134 135 krb5_ccache ccache; 136 krb5_creds creds; 137 krb5_kdc_flags flags; 138 krb5_principal principal; 139 char ret_string[10]; 140 ssize_t n; 141 142 status = krb5_auth_con_init (context, &auth_context); 143 if (status) { 144 krb5_warn (context, status, "krb5_auth_con_init"); 145 return 1; 146 } 147 148 status = krb5_auth_con_setaddrs_from_fd (context, 149 auth_context, 150 &sock); 151 if (status) { 152 krb5_warn (context, status, "krb5_auth_con_setaddr"); 153 return 1; 154 } 155 156 status = krb5_sname_to_principal (context, 157 hostname, 158 service, 159 KRB5_NT_SRV_HST, 160 &server); 161 if (status) { 162 krb5_warn (context, status, "krb5_sname_to_principal"); 163 return 1; 164 } 165 166 status = krb5_sendauth (context, 167 &auth_context, 168 &sock, 169 VERSION, 170 NULL, 171 server, 172 AP_OPTS_MUTUAL_REQUIRED, 173 NULL, 174 NULL, 175 NULL, 176 NULL, 177 NULL, 178 NULL); 179 if (status) { 180 krb5_warn(context, status, "krb5_sendauth"); 181 return 1; 182 } 183 184 if (remote_name == NULL) { 185 remote_name = get_default_username (); 186 if (remote_name == NULL) 187 errx (1, "who are you?"); 188 } 189 190 krb5_data_zero(&data_send); 191 data_send.data = (void *)remote_name; 192 data_send.length = strlen(remote_name) + 1; 193 status = krb5_write_message(context, &sock, &data_send); 194 if (status) { 195 krb5_warn (context, status, "krb5_write_message"); 196 return 1; 197 } 198 199 if (ccache_name == NULL) 200 ccache_name = ""; 201 202 data_send.data = (void *)ccache_name; 203 data_send.length = strlen(ccache_name)+1; 204 status = krb5_write_message(context, &sock, &data_send); 205 if (status) { 206 krb5_warn (context, status, "krb5_write_message"); 207 return 1; 208 } 209 210 memset (&creds, 0, sizeof(creds)); 211 212 status = krb5_cc_default (context, &ccache); 213 if (status) { 214 krb5_warn (context, status, "krb5_cc_default"); 215 return 1; 216 } 217 218 status = krb5_cc_get_principal (context, ccache, &principal); 219 if (status) { 220 krb5_warn (context, status, "krb5_cc_get_principal"); 221 return 1; 222 } 223 224 creds.client = principal; 225 226 status = krb5_build_principal (context, 227 &creds.server, 228 strlen(principal->realm), 229 principal->realm, 230 KRB5_TGS_NAME, 231 principal->realm, 232 NULL); 233 234 if (status) { 235 krb5_warn (context, status, "krb5_build_principal"); 236 return 1; 237 } 238 239 creds.times.endtime = 0; 240 241 flags.i = 0; 242 flags.b.forwarded = 1; 243 flags.b.forwardable = forwardable; 244 245 status = krb5_get_forwarded_creds (context, 246 auth_context, 247 ccache, 248 flags.i, 249 hostname, 250 &creds, 251 &data); 252 if (status) { 253 krb5_warn (context, status, "krb5_get_forwarded_creds"); 254 return 1; 255 } 256 257 status = krb5_mk_priv (context, 258 auth_context, 259 &data, 260 &packet, 261 NULL); 262 if (status) { 263 krb5_warn (context, status, "krb5_mk_priv"); 264 return 1; 265 } 266 267 len = packet.length; 268 net_len = htonl(len); 269 270 if (krb5_net_write (context, &sock, &net_len, 4) != 4) { 271 krb5_warn (context, errno, "krb5_net_write"); 272 return 1; 273 } 274 if (krb5_net_write (context, &sock, packet.data, len) != len) { 275 krb5_warn (context, errno, "krb5_net_write"); 276 return 1; 277 } 278 279 krb5_data_free (&data); 280 281 n = krb5_net_read (context, &sock, &net_len, 4); 282 if (n == 0) { 283 krb5_warnx (context, "EOF in krb5_net_read"); 284 return 1; 285 } 286 if (n < 0) { 287 krb5_warn (context, errno, "krb5_net_read"); 288 return 1; 289 } 290 len = ntohl(net_len); 291 if (len >= sizeof(ret_string)) { 292 krb5_warnx (context, "too long string back from %s", hostname); 293 return 1; 294 } 295 n = krb5_net_read (context, &sock, ret_string, len); 296 if (n == 0) { 297 krb5_warnx (context, "EOF in krb5_net_read"); 298 return 1; 299 } 300 if (n < 0) { 301 krb5_warn (context, errno, "krb5_net_read"); 302 return 1; 303 } 304 ret_string[sizeof(ret_string) - 1] = '\0'; 305 306 return(strcmp(ret_string,"ok")); 307 } 308 309 static int 310 doit (const char *hostname, int port, const char *service) 311 { 312 struct addrinfo *ai, *a; 313 struct addrinfo hints; 314 int error; 315 char portstr[NI_MAXSERV]; 316 317 memset (&hints, 0, sizeof(hints)); 318 hints.ai_socktype = SOCK_STREAM; 319 hints.ai_protocol = IPPROTO_TCP; 320 321 snprintf (portstr, sizeof(portstr), "%u", ntohs(port)); 322 323 error = getaddrinfo (hostname, portstr, &hints, &ai); 324 if (error) { 325 errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error)); 326 } 327 328 for (a = ai; a != NULL; a = a->ai_next) { 329 int s; 330 331 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 332 if (s < 0) 333 continue; 334 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 335 warn ("connect(%s)", hostname); 336 close (s); 337 continue; 338 } 339 freeaddrinfo (ai); 340 return proto (s, hostname, service); 341 } 342 warnx ("failed to contact %s", hostname); 343 freeaddrinfo (ai); 344 return 1; 345 } 346 347 int 348 main(int argc, char **argv) 349 { 350 int argcc,port,i; 351 int ret=0; 352 353 argcc = argc; 354 port = client_setup(&context, &argcc, argv); 355 356 for (i = argcc;i < argc; i++) { 357 ret = doit (argv[i], port, service); 358 warnx ("%s %s", argv[i], ret ? "failed" : "ok"); 359 } 360 return(ret); 361 } 362