/* * Kerberized NNTP connection forwarder, server side. * * $Id: knntpd.c,v 1.1 1999/12/12 08:36:55 kolya Exp $ * * kolya@MIT.EDU, 11 December 1999 */ #include "krb5.h" #include "com_err.h" #include #include #include #include #include #include #include #include #include #include "knntp.h" extern krb5_deltat krb5_clockskew; krb5_context context; krb5_auth_context auth_context = NULL; void xmit_error(char *msg, int sock) { write(sock, msg, strlen(msg)); krb5_auth_con_free(context, auth_context); krb5_free_context(context); exit(1); } void usage(char *name) { fprintf(stderr, "usage: %s [-p port] [-s service] [-S keytab]\n", name); } int main(int argc, char *argv[]) { krb5_ticket * ticket; krb5_address peeraddr; struct sockaddr_in server_sin; struct sockaddr_in peername; int namelen = sizeof(peername); int listen_sock = 0; int sock = -1; /* incoming connection fd */ int port = KNNTP_PORT; krb5_error_code retval; krb5_principal server, client; char repbuf[BUFSIZ]; char read_buf[BUFSIZ]; char *cname; char *service = KNNTP_SERVICE; extern int opterr, optind; extern char * optarg; int ch; int on = 1; krb5_keytab keytab = NULL; /* Allow specification on command line */ char *progname = *argv; int authorized = 0; fd_set all_fds; /* open a log connection */ openlog("knntpd", 0, LOG_DAEMON); /* * Parse command line arguments * */ opterr = 0; while ((ch = getopt(argc, argv, "p:S:s:")) != EOF) switch (ch) { case 'p': port = atoi(optarg); break; case 's': service = optarg; break; case 'S': if (retval = krb5_kt_resolve(context, optarg, &keytab)) { com_err(progname, retval, "while resolving keytab file %s", optarg); exit(2); } break; case '?': default: usage(progname); exit(1); break; } argc -= optind; argv += optind; listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(listen_sock < 0) { perror("creating socket"); exit(1); } setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); server_sin.sin_port = htons(port); server_sin.sin_family = AF_INET; server_sin.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listen_sock, (struct sockaddr *)&server_sin, sizeof(server_sin))) { perror("binding to socket"); exit(1); } if(listen(listen_sock, 5) < 0) { perror("listening on socket"); exit(1); } namelen = sizeof(peername); while((sock = accept(listen_sock, (struct sockaddr *)&peername, &namelen)) >= 0) { if(!fork()) break; else close(sock); } if(sock < 0) { perror("accepting connection"); exit(1); } retval = krb5_init_context(&context); if (retval) { com_err(argv[0], retval, "while initializing krb5"); exit(1); } if (retval = krb5_sname_to_principal(context, NULL, service, KRB5_NT_SRV_HST, &server)) { syslog(LOG_ERR, "while generating service name (%s): %s", service, error_message(retval)); exit(1); } /* * To verify authenticity, we need to know the address of the * client. */ peeraddr.addrtype = peername.sin_family; peeraddr.length = sizeof(peername.sin_addr); peeraddr.contents = (krb5_octet *)&peername.sin_addr; if (retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, KNNTP_VERSION, server, 0, /* no flags */ keytab, /* default keytab is NULL */ &ticket)) { syslog(LOG_ERR, "recvauth failed--%s", error_message(retval)); exit(1); } /* Get client name */ if (retval = krb5_unparse_name(context, ticket->enc_part2->client, &cname)){ syslog(LOG_ERR, "unparse failed: %s", error_message(retval)); sprintf(repbuf, "502 krb5_unparse_name() error\n"); } else { char *at_ptr; at_ptr = strchr(cname, '@'); if(!at_ptr) { sprintf(repbuf, "502 Principal missing @ character\n"); } else { if(strcmp(at_ptr+1, ALLOW_REALM)) { snprintf(repbuf, sizeof(repbuf), "502 Realm `%s' not authorized\n", at_ptr+1); } else { authorized = 1; } } free(cname); } if(!authorized) { xmit_error(repbuf, sock); } else { /* The user is authorized to access the news server; connect */ struct hostent *hp; struct sockaddr_in sin; int news_sock; news_sock = socket(AF_INET, SOCK_STREAM, 0); if(news_sock < 0) { xmit_error("502 Error creating socket\n", sock); } memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(NNTP_PORT); hp = gethostbyname(NNTP_SERVER); if(!hp) { xmit_error("502 Unable to resolve news server hostname\n", sock); } memcpy((char *)&sin.sin_addr, (char *)hp->h_addr, sizeof(hp->h_addr)); if(connect(news_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { snprintf(repbuf, sizeof(repbuf), "502 Error connecting to news server: %s\n", strerror(errno)); xmit_error(repbuf, sock); } fcntl(sock, F_SETFL, O_NONBLOCK); fcntl(news_sock, F_SETFL, O_NONBLOCK); FD_ZERO(&all_fds); FD_SET(sock, &all_fds); FD_SET(news_sock, &all_fds); while(1) { fd_set rfds = all_fds; int maxFds = MAX(sock, news_sock) + 1; retval = select(maxFds, &rfds, NULL, NULL, NULL); if(retval < 0) { snprintf(repbuf, sizeof(repbuf), "%%m: select error: %d", retval); syslog(LOG_ERR, repbuf); exit(1); } if(FD_ISSET(news_sock, &rfds)) { retval = read(news_sock, read_buf, sizeof(read_buf)); if(retval <= 0) { if(errno != EAGAIN) exit(1); } else { write(sock, read_buf, retval); } } if(FD_ISSET(sock, &rfds)) { retval = read(sock, &read_buf, sizeof(read_buf)); if(retval <= 0) { exit(1); } /* XXX assuming write() sends everything at once */ write(news_sock, read_buf, retval); } } } krb5_auth_con_free(context, auth_context); krb5_free_context(context); exit(0); }