1 /* 2 * Copyright (c) 1997 - 2000, 2002 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$"); 36 37 krb5_context context; 38 static int help_flag; 39 static int version_flag; 40 static char *port_str; 41 const char *service = KF_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, KF_PORT_NAME, "tcp", KF_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 char *message, size_t len) 127 { 128 krb5_auth_context auth_context; 129 krb5_error_code status; 130 krb5_principal server; 131 krb5_data data; 132 krb5_data data_send; 133 134 krb5_ccache ccache; 135 krb5_creds creds; 136 krb5_kdc_flags flags; 137 krb5_principal principal; 138 139 status = krb5_auth_con_init (context, &auth_context); 140 if (status) { 141 krb5_warn (context, status, "krb5_auth_con_init"); 142 return 1; 143 } 144 145 status = krb5_auth_con_setaddrs_from_fd (context, 146 auth_context, 147 &sock); 148 if (status) { 149 krb5_auth_con_free(context, auth_context); 150 krb5_warn (context, status, "krb5_auth_con_setaddr"); 151 return 1; 152 } 153 154 status = krb5_sname_to_principal (context, 155 hostname, 156 service, 157 KRB5_NT_SRV_HST, 158 &server); 159 if (status) { 160 krb5_auth_con_free(context, auth_context); 161 krb5_warn (context, status, "krb5_sname_to_principal"); 162 return 1; 163 } 164 165 status = krb5_sendauth (context, 166 &auth_context, 167 &sock, 168 KF_VERSION_1, 169 NULL, 170 server, 171 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 172 NULL, 173 NULL, 174 NULL, 175 NULL, 176 NULL, 177 NULL); 178 if (status) { 179 krb5_auth_con_free(context, auth_context); 180 krb5_warn(context, status, "krb5_sendauth"); 181 return 1; 182 } 183 184 if (ccache_name == NULL) 185 ccache_name = ""; 186 187 data_send.data = (void *)remote_name; 188 data_send.length = strlen(remote_name) + 1; 189 status = krb5_write_priv_message(context, auth_context, &sock, &data_send); 190 if (status) { 191 krb5_auth_con_free(context, auth_context); 192 krb5_warn (context, status, "krb5_write_message"); 193 return 1; 194 } 195 data_send.data = (void *)ccache_name; 196 data_send.length = strlen(ccache_name)+1; 197 status = krb5_write_priv_message(context, auth_context, &sock, &data_send); 198 if (status) { 199 krb5_auth_con_free(context, auth_context); 200 krb5_warn (context, status, "krb5_write_message"); 201 return 1; 202 } 203 204 memset (&creds, 0, sizeof(creds)); 205 206 status = krb5_cc_default (context, &ccache); 207 if (status) { 208 krb5_auth_con_free(context, auth_context); 209 krb5_warn (context, status, "krb5_cc_default"); 210 return 1; 211 } 212 213 status = krb5_cc_get_principal (context, ccache, &principal); 214 if (status) { 215 krb5_auth_con_free(context, auth_context); 216 krb5_warn (context, status, "krb5_cc_get_principal"); 217 return 1; 218 } 219 220 creds.client = principal; 221 222 status = krb5_make_principal (context, 223 &creds.server, 224 principal->realm, 225 KRB5_TGS_NAME, 226 principal->realm, 227 NULL); 228 229 if (status) { 230 krb5_auth_con_free(context, auth_context); 231 krb5_warn (context, status, "krb5_make_principal"); 232 return 1; 233 } 234 235 creds.times.endtime = 0; 236 237 flags.i = 0; 238 flags.b.forwarded = 1; 239 flags.b.forwardable = forwardable; 240 241 status = krb5_get_forwarded_creds (context, 242 auth_context, 243 ccache, 244 flags.i, 245 hostname, 246 &creds, 247 &data); 248 if (status) { 249 krb5_auth_con_free(context, auth_context); 250 krb5_warn (context, status, "krb5_get_forwarded_creds"); 251 return 1; 252 } 253 254 status = krb5_write_priv_message(context, auth_context, &sock, &data); 255 256 if (status) { 257 krb5_auth_con_free(context, auth_context); 258 krb5_warn (context, status, "krb5_mk_priv"); 259 return 1; 260 } 261 262 krb5_data_free (&data); 263 264 status = krb5_read_priv_message(context, auth_context, &sock, &data); 265 krb5_auth_con_free(context, auth_context); 266 if (status) { 267 krb5_warn (context, status, "krb5_mk_priv"); 268 return 1; 269 } 270 if(data.length >= len) { 271 krb5_warnx (context, "returned string is too long, truncating"); 272 memcpy(message, data.data, len); 273 message[len - 1] = '\0'; 274 } else { 275 memcpy(message, data.data, data.length); 276 message[data.length] = '\0'; 277 } 278 krb5_data_free (&data); 279 280 return(strcmp(message, "ok")); 281 } 282 283 static int 284 doit (const char *hostname, int port, const char *service, 285 char *message, size_t len) 286 { 287 struct addrinfo *ai, *a; 288 struct addrinfo hints; 289 int error; 290 char portstr[NI_MAXSERV]; 291 292 memset (&hints, 0, sizeof(hints)); 293 hints.ai_socktype = SOCK_STREAM; 294 hints.ai_protocol = IPPROTO_TCP; 295 296 snprintf (portstr, sizeof(portstr), "%u", ntohs(port)); 297 298 error = getaddrinfo (hostname, portstr, &hints, &ai); 299 if (error) { 300 errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error)); 301 } 302 303 for (a = ai; a != NULL; a = a->ai_next) { 304 int s; 305 306 s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); 307 if (s < 0) 308 continue; 309 if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { 310 warn ("connect(%s)", hostname); 311 close (s); 312 continue; 313 } 314 freeaddrinfo (ai); 315 return proto (s, hostname, service, message, len); 316 } 317 warnx ("failed to contact %s", hostname); 318 freeaddrinfo (ai); 319 return 1; 320 } 321 322 int 323 main(int argc, char **argv) 324 { 325 int argcc,port,i; 326 int ret=0; 327 328 argcc = argc; 329 port = client_setup(&context, &argcc, argv); 330 331 if (remote_name == NULL) { 332 remote_name = get_default_username (); 333 if (remote_name == NULL) 334 errx (1, "who are you?"); 335 } 336 337 for (i = argcc;i < argc; i++) { 338 char message[128]; 339 ret = doit (argv[i], port, service, message, sizeof(message)); 340 if(ret == 0) 341 warnx ("%s: ok", argv[i]); 342 else 343 warnx ("%s: failed: %s", argv[i], message); 344 } 345 return(ret); 346 } 347