1 /* 2 * Copyright (c) 1997 - 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 char krb5_tkfile[MAXPATHLEN]; 39 40 static int help_flag; 41 static int version_flag; 42 static char *port_str; 43 char *service = KF_SERVICE; 44 int do_inetd = 0; 45 static char *regpag_str=NULL; 46 47 static struct getargs args[] = { 48 { "port", 'p', arg_string, &port_str, "port to listen to", "port" }, 49 { "inetd",'i',arg_flag, &do_inetd, 50 "Not started from inetd", NULL }, 51 { "regpag",'R',arg_string,®pag_str,"path to regpag binary","regpag"}, 52 { "help", 'h', arg_flag, &help_flag }, 53 { "version", 0, arg_flag, &version_flag } 54 }; 55 56 static int num_args = sizeof(args) / sizeof(args[0]); 57 58 static void 59 usage(int code, struct getargs *args, int num_args) 60 { 61 arg_printusage(args, num_args, NULL, ""); 62 exit(code); 63 } 64 65 static int 66 server_setup(krb5_context *context, int argc, char **argv) 67 { 68 int port = 0; 69 int local_argc; 70 71 local_argc = krb5_program_setup(context, argc, argv, args, num_args, usage); 72 73 if(help_flag) 74 (*usage)(0, args, num_args); 75 if(version_flag) { 76 print_version(NULL); 77 exit(0); 78 } 79 80 if(port_str){ 81 struct servent *s = roken_getservbyname(port_str, "tcp"); 82 if(s) 83 port = s->s_port; 84 else { 85 char *ptr; 86 87 port = strtol (port_str, &ptr, 10); 88 if (port == 0 && ptr == port_str) 89 errx (1, "Bad port `%s'", port_str); 90 port = htons(port); 91 } 92 } 93 94 if (port == 0) 95 port = krb5_getportbyname (*context, KF_PORT_NAME, "tcp", KF_PORT_NUM); 96 97 if(argv[local_argc] != NULL) 98 usage(1, args, num_args); 99 100 return port; 101 } 102 103 static int protocol_version; 104 105 static krb5_boolean 106 kfd_match_version(const void *arg, const char *version) 107 { 108 if(strcmp(version, KF_VERSION_1) == 0) { 109 protocol_version = 1; 110 return TRUE; 111 } else if (strlen(version) == 4 && 112 version[0] == '0' && 113 version[1] == '.' && 114 (version[2] == '4' || version[2] == '3') && 115 islower((unsigned char)version[3])) { 116 protocol_version = 0; 117 return TRUE; 118 } 119 return FALSE; 120 } 121 122 static int 123 proto (int sock, const char *service) 124 { 125 krb5_auth_context auth_context; 126 krb5_error_code status; 127 krb5_principal server; 128 krb5_ticket *ticket; 129 char *name; 130 char ret_string[10]; 131 char hostname[MAXHOSTNAMELEN]; 132 krb5_data data; 133 krb5_data remotename; 134 krb5_data tk_file; 135 krb5_ccache ccache; 136 char ccname[MAXPATHLEN]; 137 struct passwd *pwd; 138 139 status = krb5_auth_con_init (context, &auth_context); 140 if (status) 141 krb5_err(context, 1, status, "krb5_auth_con_init"); 142 143 status = krb5_auth_con_setaddrs_from_fd (context, 144 auth_context, 145 &sock); 146 if (status) 147 krb5_err(context, 1, status, "krb5_auth_con_setaddr"); 148 149 if(gethostname (hostname, sizeof(hostname)) < 0) 150 krb5_err(context, 1, errno, "gethostname"); 151 152 status = krb5_sname_to_principal (context, 153 hostname, 154 service, 155 KRB5_NT_SRV_HST, 156 &server); 157 if (status) 158 krb5_err(context, 1, status, "krb5_sname_to_principal"); 159 160 status = krb5_recvauth_match_version (context, 161 &auth_context, 162 &sock, 163 kfd_match_version, 164 NULL, 165 server, 166 0, 167 NULL, 168 &ticket); 169 if (status) 170 krb5_err(context, 1, status, "krb5_recvauth"); 171 172 status = krb5_unparse_name (context, 173 ticket->client, 174 &name); 175 if (status) 176 krb5_err(context, 1, status, "krb5_unparse_name"); 177 178 if(protocol_version == 0) { 179 data.data = "old clnt"; /* XXX old clients only had room for 180 10 bytes of message, and also 181 didn't show it to the user */ 182 data.length = strlen(data.data) + 1; 183 krb5_write_message(context, &sock, &data); 184 sleep(2); /* XXX give client time to finish */ 185 krb5_errx(context, 1, "old client; exiting"); 186 } 187 188 status=krb5_read_priv_message (context, auth_context, 189 &sock, &remotename); 190 if (status) 191 krb5_err(context, 1, status, "krb5_read_message"); 192 status=krb5_read_priv_message (context, auth_context, 193 &sock, &tk_file); 194 if (status) 195 krb5_err(context, 1, status, "krb5_read_message"); 196 197 krb5_data_zero (&data); 198 199 if(((char*)remotename.data)[remotename.length-1] != '\0') 200 krb5_errx(context, 1, "unterminated received"); 201 if(((char*)tk_file.data)[tk_file.length-1] != '\0') 202 krb5_errx(context, 1, "unterminated received"); 203 204 status = krb5_read_priv_message(context, auth_context, &sock, &data); 205 206 if (status) { 207 krb5_err(context, 1, errno, "krb5_read_priv_message"); 208 goto out; 209 } 210 211 pwd = getpwnam ((char *)(remotename.data)); 212 if (pwd == NULL) { 213 status=1; 214 krb5_warnx(context, "getpwnam: %s failed",(char *)(remotename.data)); 215 goto out; 216 } 217 218 if(!krb5_kuserok (context, 219 ticket->client, 220 (char *)(remotename.data))) { 221 status=1; 222 krb5_warnx(context, "krb5_kuserok: permission denied"); 223 goto out; 224 } 225 226 if (setgid(pwd->pw_gid) < 0) { 227 krb5_warn(context, errno, "setgid"); 228 goto out; 229 } 230 if (setuid(pwd->pw_uid) < 0) { 231 krb5_warn(context, errno, "setuid"); 232 goto out; 233 } 234 235 if (tk_file.length != 1) 236 snprintf (ccname, sizeof(ccname), "%s", (char *)(tk_file.data)); 237 else 238 snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%lu", 239 (unsigned long)pwd->pw_uid); 240 241 status = krb5_cc_resolve (context, ccname, &ccache); 242 if (status) { 243 krb5_warn(context, status, "krb5_cc_resolve"); 244 goto out; 245 } 246 status = krb5_cc_initialize (context, ccache, ticket->client); 247 if (status) { 248 krb5_warn(context, status, "krb5_cc_initialize"); 249 goto out; 250 } 251 status = krb5_rd_cred2 (context, auth_context, ccache, &data); 252 krb5_cc_close (context, ccache); 253 if (status) { 254 krb5_warn(context, status, "krb5_rd_cred"); 255 goto out; 256 257 } 258 strlcpy(krb5_tkfile,ccname,sizeof(krb5_tkfile)); 259 krb5_warnx(context, "%s forwarded ticket to %s,%s", 260 name, 261 (char *)(remotename.data),ccname); 262 out: 263 if (status) { 264 strlcpy(ret_string, "no", sizeof(ret_string)); 265 krb5_warnx(context, "failed"); 266 } else { 267 strlcpy(ret_string, "ok", sizeof(ret_string)); 268 } 269 270 krb5_data_free (&tk_file); 271 krb5_data_free (&remotename); 272 krb5_data_free (&data); 273 free(name); 274 275 data.data = ret_string; 276 data.length = strlen(ret_string) + 1; 277 status = krb5_write_priv_message(context, auth_context, &sock, &data); 278 krb5_auth_con_free(context, auth_context); 279 280 return status; 281 } 282 283 static int 284 doit (int port, const char *service) 285 { 286 if (do_inetd) 287 mini_inetd(port, NULL); 288 return proto (STDIN_FILENO, service); 289 } 290 291 int 292 main(int argc, char **argv) 293 { 294 int port; 295 int ret; 296 krb5_log_facility *fac; 297 298 setprogname (argv[0]); 299 roken_openlog (argv[0], LOG_ODELAY | LOG_PID,LOG_AUTH); 300 port = server_setup(&context, argc, argv); 301 ret = krb5_openlog(context, "kfd", &fac); 302 if(ret) krb5_err(context, 1, ret, "krb5_openlog"); 303 ret = krb5_set_warn_dest(context, fac); 304 if(ret) krb5_err(context, 1, ret, "krb5_set_warn_dest"); 305 306 ret = doit (port, service); 307 closelog(); 308 if (ret == 0 && regpag_str != NULL) 309 ret = execl(regpag_str, "regpag", "-t", krb5_tkfile, "-r", NULL); 310 return ret; 311 } 312