/* * Copyright (c) 1989 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include RCSID("$Id: pop_init.c,v 1.1.1.2 2000/12/29 01:42:46 assar Exp $"); #if defined(KRB4) || defined(KRB5) static int pop_net_read(POP *p, int fd, void *buf, size_t len) { #ifdef KRB5 return krb5_net_read(p->context, &fd, buf, len); #elif defined(KRB4) return krb_net_read(fd, buf, len); #endif } #endif #ifdef KRB4 static int krb4_authenticate (POP *p, int s, u_char *buf, struct sockaddr *addr) { Key_schedule schedule; KTEXT_ST ticket; char instance[INST_SZ]; char version[9]; int auth; if (memcmp (buf, KRB_SENDAUTH_VERS, 4) != 0) return -1; if (pop_net_read (p, s, buf + 4, KRB_SENDAUTH_VLEN - 4) != KRB_SENDAUTH_VLEN - 4) return -1; if (memcmp (buf, KRB_SENDAUTH_VERS, KRB_SENDAUTH_VLEN) != 0) return -1; k_getsockinst (0, instance, sizeof(instance)); auth = krb_recvauth(KOPT_IGNORE_PROTOCOL, s, &ticket, "pop", instance, (struct sockaddr_in *)addr, (struct sockaddr_in *) NULL, &p->kdata, "", schedule, version); if (auth != KSUCCESS) { pop_msg(p, POP_FAILURE, "Kerberos authentication failure: %s", krb_get_err_text(auth)); pop_log(p, POP_PRIORITY, "%s: (%s.%s@%s) %s", p->client, p->kdata.pname, p->kdata.pinst, p->kdata.prealm, krb_get_err_text(auth)); exit (1); } #ifdef DEBUG pop_log(p, POP_DEBUG, "%s.%s@%s (%s): ok", p->kdata.pname, p->kdata.pinst, p->kdata.prealm, p->ipaddr); #endif /* DEBUG */ return 0; } #endif /* KRB4 */ #ifdef KRB5 static int krb5_authenticate (POP *p, int s, u_char *buf, struct sockaddr *addr) { krb5_error_code ret; krb5_auth_context auth_context = NULL; u_int32_t len; krb5_ticket *ticket; if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0) return -1; len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); if (krb5_net_read(p->context, &s, buf, len) != len) return -1; if (len != sizeof(KRB5_SENDAUTH_VERSION) || memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0) return -1; ret = krb5_recvauth (p->context, &auth_context, &s, "KPOPV1.0", NULL, /* let rd_req figure out what server to use */ KRB5_RECVAUTH_IGNORE_VERSION, NULL, &ticket); if (ret == 0) { char *s; ret = krb5_unparse_name(p->context, ticket->server, &s); if(ret) { pop_log(p, POP_PRIORITY, "krb5_unparse_name: %s", krb5_get_err_text(p->context, ret)); exit(1); } /* does this make sense? */ if(strncmp(server, "pop/", 4) != 0) { pop_log(p, POP_PRIORITY, "Got ticket for service `%s'", server); exit(1); } else if(p->debug) pop_log(p, POP_DEBUG, "Accepted ticket for service `%s'", s); free(s); krb5_auth_con_free (p->context, auth_context); krb5_copy_principal (p->context, ticket->client, &p->principal); krb5_free_ticket (p->context, ticket); } return ret; } #endif static int krb_authenticate(POP *p, struct sockaddr *addr) { #if defined(KRB4) || defined(KRB5) u_char buf[BUFSIZ]; if (pop_net_read (p, 0, buf, 4) != 4) { pop_msg(p, POP_FAILURE, "Reading four bytes: %s", strerror(errno)); exit (1); } #ifdef KRB4 if (krb4_authenticate (p, 0, buf, addr) == 0){ p->version = 4; return POP_SUCCESS; } #endif #ifdef KRB5 if (krb5_authenticate (p, 0, buf, addr) == 0){ p->version = 5; return POP_SUCCESS; } #endif exit (1); #endif /* defined(KRB4) || defined(KRB5) */ return(POP_SUCCESS); } static int plain_authenticate (POP *p, struct sockaddr *addr) { return(POP_SUCCESS); } static int kerberos_flag; static char *auth_str; static int debug_flag; static int interactive_flag; static char *port_str; static char *trace_file; static int timeout; static int help_flag; static int version_flag; static struct getargs args[] = { #if defined(KRB4) || defined(KRB5) { "kerberos", 'k', arg_flag, &kerberos_flag, "use kerberos" }, #endif { "auth-mode", 'a', arg_string, &auth_str, "required authentication" }, { "debug", 'd', arg_flag, &debug_flag }, { "interactive", 'i', arg_flag, &interactive_flag, "create new socket" }, { "port", 'p', arg_string, &port_str, "port to listen to", "port" }, { "trace-file", 't', arg_string, &trace_file, "trace all command to file", "file" }, { "timeout", 'T', arg_integer, &timeout, "timeout", "seconds" }, { "help", 'h', arg_flag, &help_flag }, { "version", 'v', arg_flag, &version_flag } }; static int num_args = sizeof(args) / sizeof(args[0]); /* * init: Start a Post Office Protocol session */ static int pop_getportbyname(POP *p, const char *service, const char *proto, short def) { #ifdef KRB5 return krb5_getportbyname(p->context, service, proto, def); #elif defined(KRB4) return k_getportbyname(service, proto, htons(def)); #else return htons(default); #endif } int pop_init(POP *p,int argcount,char **argmessage) { struct sockaddr_storage cs_ss; struct sockaddr *cs = (struct sockaddr *)&cs_ss; struct hostent * ch; /* Client host information */ int len; char * trace_file_name = "/tmp/popper-trace"; int portnum = 0; int optind = 0; int error; /* Initialize the POP parameter block */ memset (p, 0, sizeof(POP)); set_progname(argmessage[0]); /* Save my name in a global variable */ p->myname = (char*)__progname; /* Get the name of our host */ gethostname(p->myhost,MaxHostNameLen); #ifdef KRB5 krb5_init_context (&p->context); krb5_openlog(p->context, p->myname, &p->logf); krb5_set_warn_dest(p->context, p->logf); #else /* Open the log file */ roken_openlog(p->myname,POP_LOGOPTS,POP_FACILITY); #endif p->auth_level = AUTH_NONE; if(getarg(args, num_args, argcount, argmessage, &optind)){ arg_printusage(args, num_args, NULL, ""); exit(1); } if(help_flag){ arg_printusage(args, num_args, NULL, ""); exit(0); } if(version_flag){ print_version(NULL); exit(0); } argcount -= optind; argmessage += optind; if (argcount != 0) { arg_printusage(args, num_args, NULL, ""); exit(1); } if(auth_str){ if (strcmp (auth_str, "none") == 0) p->auth_level = AUTH_NONE; else if(strcmp(auth_str, "otp") == 0) p->auth_level = AUTH_OTP; else warnx ("bad value for -a: %s", optarg); } /* Debugging requested */ p->debug = debug_flag; if(port_str) portnum = htons(atoi(port_str)); if(trace_file){ p->debug++; if ((p->trace = fopen(trace_file, "a+")) == NULL) { pop_log(p, POP_PRIORITY, "Unable to open trace file \"%s\", err = %d", optarg,errno); exit (1); } trace_file_name = trace_file; } #if defined(KRB4) || defined(KRB5) p->kerberosp = kerberos_flag; #endif if(timeout) pop_timeout = timeout; /* Fake inetd */ if (interactive_flag) { if (portnum == 0) portnum = p->kerberosp ? pop_getportbyname(p, "kpop", "tcp", 1109) : pop_getportbyname(p, "pop", "tcp", 110); mini_inetd (portnum); } /* Get the address and socket of the client to whom I am speaking */ len = sizeof(cs_ss); if (getpeername(STDIN_FILENO, cs, &len) < 0) { pop_log(p,POP_PRIORITY, "Unable to obtain socket and address of client, err = %d",errno); exit (1); } /* Save the dotted decimal form of the client's IP address in the POP parameter block */ inet_ntop (cs->sa_family, socket_get_address (cs), p->ipaddr, sizeof(p->ipaddr)); /* Save the client's port */ p->ipport = ntohs(socket_get_port (cs)); /* Get the canonical name of the host to whom I am speaking */ ch = getipnodebyaddr (socket_get_address (cs), socket_addr_size (cs), cs->sa_family, &error); if (ch == NULL){ pop_log(p,POP_PRIORITY, "Unable to get canonical name of client, err = %d",error); strlcpy (p->client, p->ipaddr, sizeof(p->client)); } /* Save the cannonical name of the client host in the POP parameter block */ else { /* Distrust distant nameservers */ struct hostent * ch_again; char * * addrp; /* See if the name obtained for the client's IP address returns an address */ ch_again = getipnodebyname (ch->h_name, cs->sa_family, 0, &error); if (ch_again == NULL) { pop_log(p,POP_PRIORITY, "Client at \"%s\" resolves to an unknown host name \"%s\"", p->ipaddr,ch->h_name); strlcpy (p->client, p->ipaddr, sizeof(p->client)); } else { /* Save the host name (the previous value was destroyed by gethostbyname) */ strlcpy (p->client, ch->h_name, sizeof(p->client)); /* Look for the client's IP address in the list returned for its name */ for (addrp=ch_again->h_addr_list; *addrp; ++addrp) if (memcmp(*addrp, socket_get_address (cs), socket_addr_size (cs)) == 0) break; if (!*addrp) { pop_log (p,POP_PRIORITY, "Client address \"%s\" not listed for its host name \"%s\"", p->ipaddr,ch->h_name); strlcpy (p->client, p->ipaddr, sizeof(p->client)); } } freehostent (ch_again); } if(ch != NULL) freehostent (ch); /* Create input file stream for TCP/IP communication */ if ((p->input = fdopen(STDIN_FILENO,"r")) == NULL){ pop_log(p,POP_PRIORITY, "Unable to open communication stream for input, err = %d",errno); exit (1); } /* Create output file stream for TCP/IP communication */ if ((p->output = fdopen(STDOUT_FILENO,"w")) == NULL){ pop_log(p,POP_PRIORITY, "Unable to open communication stream for output, err = %d",errno); exit (1); } pop_log(p,POP_PRIORITY, "(v%s) Servicing request from \"%s\" at %s\n", VERSION,p->client,p->ipaddr); #ifdef DEBUG if (p->trace) pop_log(p,POP_PRIORITY, "Tracing session and debugging information in file \"%s\"", trace_file_name); else if (p->debug) pop_log(p,POP_PRIORITY,"Debugging turned on"); #endif /* DEBUG */ return((p->kerberosp ? krb_authenticate : plain_authenticate)(p, cs)); }