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: kfd.c,v 1.11 2003/04/16 15:40:24 lha Exp $"); 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(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_%u",pwd->pw_uid); 239 240 status = krb5_cc_resolve (context, ccname, &ccache); 241 if (status) { 242 krb5_warn(context, status, "krb5_cc_resolve"); 243 goto out; 244 } 245 status = krb5_cc_initialize (context, ccache, ticket->client); 246 if (status) { 247 krb5_warn(context, status, "krb5_cc_initialize"); 248 goto out; 249 } 250 status = krb5_rd_cred2 (context, auth_context, ccache, &data); 251 krb5_cc_close (context, ccache); 252 if (status) { 253 krb5_warn(context, status, "krb5_rd_cred"); 254 goto out; 255 256 } 257 strlcpy(krb5_tkfile,ccname,sizeof(krb5_tkfile)); 258 krb5_warnx(context, "%s forwarded ticket to %s,%s", 259 name, 260 (char *)(remotename.data),ccname); 261 out: 262 if (status) { 263 strlcpy(ret_string, "no", sizeof(ret_string)); 264 krb5_warnx(context, "failed"); 265 } else { 266 strlcpy(ret_string, "ok", sizeof(ret_string)); 267 } 268 269 krb5_data_free (&tk_file); 270 krb5_data_free (&remotename); 271 krb5_data_free (&data); 272 free(name); 273 274 data.data = ret_string; 275 data.length = strlen(ret_string) + 1; 276 return krb5_write_priv_message(context, auth_context, &sock, &data); 277 } 278 279 static int 280 doit (int port, const char *service) 281 { 282 if (do_inetd) 283 mini_inetd(port); 284 return proto (STDIN_FILENO, service); 285 } 286 287 int 288 main(int argc, char **argv) 289 { 290 int port; 291 int ret; 292 krb5_log_facility *fac; 293 294 setprogname (argv[0]); 295 roken_openlog (argv[0], LOG_ODELAY | LOG_PID,LOG_AUTH); 296 port = server_setup(&context, argc, argv); 297 ret = krb5_openlog(context, "kfd", &fac); 298 if(ret) krb5_err(context, 1, ret, "krb5_openlog"); 299 ret = krb5_set_warn_dest(context, fac); 300 if(ret) krb5_err(context, 1, ret, "krb5_set_warn_dest"); 301 302 ret = doit (port, service); 303 closelog(); 304 if (ret == 0 && regpag_str != NULL) 305 ret = execl(regpag_str, "regpag", "-t", krb5_tkfile, "-r", NULL); 306 return ret; 307 } 308