1/* 2 * Copyright (c) 1989 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7#include <popper.h> 8RCSID("$Id$"); 9 10 11#if defined(KRB5) 12 13static int 14pop_net_read(POP *p, int fd, void *buf, size_t len) 15{ 16#ifdef KRB5 17 return krb5_net_read(p->context, &fd, buf, len); 18#else 19#error must define KRB5 20#endif 21} 22#endif 23 24static char *addr_log; 25 26static void 27pop_write_addr(POP *p, struct sockaddr *addr) 28{ 29 char ts[32]; 30 char as[128]; 31 time_t t; 32 FILE *f; 33 if(addr_log == NULL) 34 return; 35 t = time(NULL); 36 strftime(ts, sizeof(ts), "%Y%m%d%H%M%S", localtime(&t)); 37 if(inet_ntop (addr->sa_family, socket_get_address(addr), 38 as, sizeof(as)) == NULL) { 39 pop_log(p, POP_PRIORITY, "failed to print address"); 40 return; 41 } 42 43 f = fopen(addr_log, "a"); 44 if(f == NULL) { 45 pop_log(p, POP_PRIORITY, "failed to open address log (%s)", addr_log); 46 return; 47 } 48 fprintf(f, "%s %s\n", as, ts); 49 fclose(f); 50} 51 52#ifdef KRB5 53static int 54krb5_authenticate (POP *p, int s, u_char *buf, struct sockaddr *addr) 55{ 56 krb5_error_code ret; 57 krb5_auth_context auth_context = NULL; 58 uint32_t len; 59 krb5_ticket *ticket; 60 char *server; 61 62 if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0) 63 return -1; 64 len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); 65 66 if (krb5_net_read(p->context, &s, buf, len) != len) 67 return -1; 68 if (len != sizeof(KRB5_SENDAUTH_VERSION) 69 || memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0) 70 return -1; 71 72 ret = krb5_recvauth (p->context, 73 &auth_context, 74 &s, 75 "KPOPV1.0", 76 NULL, /* let rd_req figure out what server to use */ 77 KRB5_RECVAUTH_IGNORE_VERSION, 78 NULL, 79 &ticket); 80 if (ret) { 81 pop_log(p, POP_PRIORITY, "krb5_recvauth: %s", 82 krb5_get_err_text(p->context, ret)); 83 return -1; 84 } 85 86 87 ret = krb5_unparse_name(p->context, ticket->server, &server); 88 if(ret) { 89 pop_log(p, POP_PRIORITY, "krb5_unparse_name: %s", 90 krb5_get_err_text(p->context, ret)); 91 ret = -1; 92 goto out; 93 } 94 /* does this make sense? */ 95 if(strncmp(server, "pop/", 4) != 0) { 96 pop_log(p, POP_PRIORITY, 97 "Got ticket for service `%s'", server); 98 ret = -1; 99 goto out; 100 } else if(p->debug) 101 pop_log(p, POP_DEBUG, 102 "Accepted ticket for service `%s'", server); 103 free(server); 104 out: 105 krb5_auth_con_free (p->context, auth_context); 106 krb5_copy_principal (p->context, ticket->client, &p->principal); 107 krb5_free_ticket (p->context, ticket); 108 109 return ret; 110} 111#endif 112 113static int 114krb_authenticate(POP *p, struct sockaddr *addr) 115{ 116#if defined(KRB5) 117 u_char buf[BUFSIZ]; 118 119 if (pop_net_read (p, 0, buf, 4) != 4) { 120 pop_msg(p, POP_FAILURE, "Reading four bytes: %s", 121 strerror(errno)); 122 exit (1); 123 } 124 if (krb5_authenticate (p, 0, buf, addr) == 0){ 125 pop_write_addr(p, addr); 126 p->version = 5; 127 return POP_SUCCESS; 128 } 129#endif 130 exit (1); 131 132 return(POP_SUCCESS); 133} 134 135static int 136plain_authenticate (POP *p, struct sockaddr *addr) 137{ 138 return(POP_SUCCESS); 139} 140 141static int kerberos_flag; 142static char *auth_str; 143static int debug_flag; 144static int interactive_flag; 145static char *port_str; 146static char *trace_file; 147static int timeout; 148static int help_flag; 149static int version_flag; 150 151static struct getargs args[] = { 152#if defined(KRB5) 153 { "kerberos", 'k', arg_flag, &kerberos_flag, "use kerberos" }, 154#endif 155 { "auth-mode", 'a', arg_string, &auth_str, "required authentication", 156 "plaintext" 157#ifdef OTP 158 "|otp" 159#endif 160#ifdef SASL 161 "|sasl" 162#endif 163 }, 164 { "debug", 'd', arg_flag, &debug_flag }, 165 { "interactive", 'i', arg_flag, &interactive_flag, "create new socket" }, 166 { "port", 'p', arg_string, &port_str, "port to listen to", "port" }, 167 { "trace-file", 't', arg_string, &trace_file, "trace all command to file", "file" }, 168 { "timeout", 'T', arg_integer, &timeout, "timeout", "seconds" }, 169 { "address-log", 0, arg_string, &addr_log, "enable address log", "file" }, 170 { "help", 'h', arg_flag, &help_flag }, 171 { "version", 'v', arg_flag, &version_flag } 172}; 173 174static int num_args = sizeof(args) / sizeof(args[0]); 175 176/* 177 * init: Start a Post Office Protocol session 178 */ 179 180static int 181pop_getportbyname(POP *p, const char *service, 182 const char *proto, short def) 183{ 184#ifdef KRB5 185 return krb5_getportbyname(p->context, service, proto, def); 186#else 187 return htons(default); 188#endif 189} 190 191int 192pop_init(POP *p,int argcount,char **argmessage) 193{ 194 struct sockaddr_storage cs_ss; 195 struct sockaddr *cs = (struct sockaddr *)&cs_ss; 196 socklen_t len; 197 char * trace_file_name = "/tmp/popper-trace"; 198 int portnum = 0; 199 int optind = 0; 200 int error; 201 202 /* Initialize the POP parameter block */ 203 memset (p, 0, sizeof(POP)); 204 205 setprogname(argmessage[0]); 206 207 /* Save my name in a global variable */ 208 p->myname = (char*)getprogname(); 209 210 /* Get the name of our host */ 211 gethostname(p->myhost,MaxHostNameLen); 212 213#ifdef KRB5 214 { 215 krb5_error_code ret; 216 217 ret = krb5_init_context (&p->context); 218 if (ret) 219 errx (1, "krb5_init_context failed: %d", ret); 220 221 krb5_openlog(p->context, p->myname, &p->logf); 222 krb5_set_warn_dest(p->context, p->logf); 223 } 224#else 225 /* Open the log file */ 226 roken_openlog(p->myname,POP_LOGOPTS,POP_FACILITY); 227#endif 228 229 p->auth_level = AUTH_NONE; 230 231 if(getarg(args, num_args, argcount, argmessage, &optind)){ 232 arg_printusage(args, num_args, NULL, ""); 233 exit(1); 234 } 235 if(help_flag){ 236 arg_printusage(args, num_args, NULL, ""); 237 exit(0); 238 } 239 if(version_flag){ 240 print_version(NULL); 241 exit(0); 242 } 243 244 argcount -= optind; 245 argmessage += optind; 246 247 if (argcount != 0) { 248 arg_printusage(args, num_args, NULL, ""); 249 exit(1); 250 } 251 252 if(auth_str){ 253 if (strcasecmp (auth_str, "plaintext") == 0 || 254 strcasecmp (auth_str, "none") == 0) 255 p->auth_level = AUTH_NONE; 256 else if(strcasecmp(auth_str, "otp") == 0) { 257#ifdef OTP 258 p->auth_level = AUTH_OTP; 259#else 260 pop_log (p, POP_PRIORITY, "support for OTP not enabled"); 261 exit(1); 262#endif 263 } else if(strcasecmp(auth_str, "sasl") == 0) { 264#ifdef SASL 265 p->auth_level = AUTH_SASL; 266#else 267 pop_log (p, POP_PRIORITY, "support for SASL not enabled"); 268 exit(1); 269#endif 270 } else { 271 pop_log (p, POP_PRIORITY, "bad value for -a: %s", auth_str); 272 exit(1); 273 } 274 } 275 /* Debugging requested */ 276 p->debug = debug_flag; 277 278 if(port_str) 279 portnum = htons(atoi(port_str)); 280 if(trace_file){ 281 p->debug++; 282 if ((p->trace = fopen(trace_file, "a+")) == NULL) { 283 pop_log(p, POP_PRIORITY, 284 "Unable to open trace file \"%s\", err = %d", 285 optarg,errno); 286 exit (1); 287 } 288 trace_file_name = trace_file; 289 } 290 291#if defined(KRB5) 292 p->kerberosp = kerberos_flag; 293#endif 294 295 if(timeout) 296 pop_timeout = timeout; 297 298 /* Fake inetd */ 299 if (interactive_flag) { 300 if (portnum == 0) 301 portnum = p->kerberosp ? 302 pop_getportbyname(p, "kpop", "tcp", 1109) : 303 pop_getportbyname(p, "pop", "tcp", 110); 304 mini_inetd (portnum, NULL); 305 } 306 307 /* Get the address and socket of the client to whom I am speaking */ 308 len = sizeof(cs_ss); 309 if (getpeername(STDIN_FILENO, cs, &len) < 0) { 310 pop_log(p,POP_PRIORITY, 311 "Unable to obtain socket and address of client, err = %d",errno); 312 exit (1); 313 } 314 315 /* Save the dotted decimal form of the client's IP address 316 in the POP parameter block */ 317 inet_ntop (cs->sa_family, socket_get_address (cs), 318 p->ipaddr, sizeof(p->ipaddr)); 319 320 /* Save the client's port */ 321 p->ipport = ntohs(socket_get_port (cs)); 322 323 /* Get the canonical name of the host to whom I am speaking */ 324 error = getnameinfo_verified (cs, len, p->client, sizeof(p->client), 325 NULL, 0, 0); 326 if (error) { 327 pop_log (p, POP_PRIORITY, 328 "getnameinfo: %s", gai_strerror (error)); 329 strlcpy (p->client, p->ipaddr, sizeof(p->client)); 330 } 331 332 /* Create input file stream for TCP/IP communication */ 333 if ((p->input = fdopen(STDIN_FILENO,"r")) == NULL){ 334 pop_log(p,POP_PRIORITY, 335 "Unable to open communication stream for input, err = %d",errno); 336 exit (1); 337 } 338 339 /* Create output file stream for TCP/IP communication */ 340 if ((p->output = fdopen(STDOUT_FILENO,"w")) == NULL){ 341 pop_log(p,POP_PRIORITY, 342 "Unable to open communication stream for output, err = %d",errno); 343 exit (1); 344 } 345 346 pop_log(p,POP_PRIORITY, 347 "(v%s) Servicing request from \"%s\" at %s\n", 348 VERSION,p->client,p->ipaddr); 349 350#ifdef DEBUG 351 if (p->trace) 352 pop_log(p,POP_PRIORITY, 353 "Tracing session and debugging information in file \"%s\"", 354 trace_file_name); 355 else if (p->debug) 356 pop_log(p,POP_PRIORITY,"Debugging turned on"); 357#endif /* DEBUG */ 358 359 360 if(p->kerberosp) 361 return krb_authenticate(p, cs); 362 else 363 return plain_authenticate(p, cs); 364} 365