📄 krb4-security.c
字号:
* for our network fd. */static voidkrb4_recvpkt_cancel( void * cookie){ struct krb4_handle *kh = cookie; assert(kh != NULL); if (kh->fn != NULL) { handleq_remove(kh); kh->fn = NULL; kh->arg = NULL; } if (kh->ev_timeout != NULL) event_release(kh->ev_timeout); kh->ev_timeout = NULL; if (handleq.qlength == 0 && accept_fn == NULL && ev_netfd != NULL) { event_release(ev_netfd); ev_netfd = NULL; }}/* * Create the server end of a stream. For krb4, this means setup a tcp * socket for receiving a connection. */static void *krb4_stream_server( void * h){ struct krb4_handle *kh = h; struct krb4_stream *ks; assert(kh != NULL); ks = alloc(SIZEOF(*ks)); security_streaminit(&ks->secstr, &krb4_security_driver); ks->socket = stream_server(&ks->port, STREAM_BUFSIZE, STREAM_BUFSIZE, 1); if (ks->socket < 0) { security_seterror(&kh->sech, _("can't create server stream: %s"), strerror(errno)); amfree(ks); return (NULL); } ks->fd = -1; ks->krb4_handle = kh; ks->ev_read = NULL; return (ks);}/* * Accept an incoming connection on a stream_server socket */static intkrb4_stream_accept( void * s){ struct krb4_stream *ks = s; struct krb4_handle *kh; assert(ks != NULL); kh = ks->krb4_handle; assert(kh != NULL); assert(ks->socket >= 0); assert(ks->fd == -1); ks->fd = stream_accept(ks->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE); if (ks->fd < 0) { security_stream_seterror(&ks->secstr, _("can't accept new stream connection: %s"), strerror(errno)); return (-1); } return (0);}/* * Return a connected stream. */static void *krb4_stream_client( void * h, int id){ struct krb4_handle *kh = h; struct krb4_stream *ks; assert(kh != NULL); ks = alloc(SIZEOF(*ks)); security_streaminit(&ks->secstr, &krb4_security_driver); ks->fd = stream_client(kh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE, &ks->port, 0); if (ks->fd < 0) { security_seterror(&kh->sech, _("can't connect stream to %s port %d: %s"), kh->hostname, id, strerror(errno)); amfree(ks); return (NULL); } ks->socket = -1; /* we're a client */ ks->krb4_handle = kh; ks->ev_read = NULL; return (ks);}/* * Close and unallocate resources for a stream. */static voidkrb4_stream_close( void * s){ struct krb4_stream *ks = s; assert(ks != NULL); if (ks->fd != -1) aclose(ks->fd); if (ks->socket != -1) aclose(ks->socket); krb4_stream_read_cancel(ks); amfree(ks);}/* * Authenticate a stream * * XXX this whole thing assumes the size of struct timeval is consistent, * which is may not be! We need to extract the network byte order data * into byte arrays and send those. */static intkrb4_stream_auth( void * s){ struct krb4_stream *ks = s; struct krb4_handle *kh; int fd; struct timeval local, enc; struct timezone tz; assert(ks != NULL); assert(ks->fd >= 0); fd = ks->fd; kh = ks->krb4_handle; /* make sure we're open */ assert(fd >= 0); /* make sure our timeval is what we're expecting, see above */ assert(SIZEOF(struct timeval) == 8); /* * Get the current time, put it in network byte order, encrypt it * and present it to the other side. */ gettimeofday(&local, &tz); enc.tv_sec = (long)htonl((guint32)local.tv_sec); enc.tv_usec = (long)htonl((guint32)local.tv_usec); encrypt_data(&enc, SIZEOF(enc), &kh->session_key); if (knet_write(fd, &enc, SIZEOF(enc)) < 0) { security_stream_seterror(&ks->secstr, _("krb4 stream handshake write error: %s"), strerror(errno)); return (-1); } /* * Read back the other side's presentation. Increment the seconds * and useconds by one. Reencrypt, and present to the other side. * Timeout in 10 seconds. */ if (knet_read(fd, &enc, SIZEOF(enc), 60) < 0) { security_stream_seterror(&ks->secstr, _("krb4 stream handshake read error: %s"), strerror(errno)); return (-1); } decrypt_data(&enc, SIZEOF(enc), &kh->session_key); /* XXX do timestamp checking here */ enc.tv_sec = (long)htonl(ntohl((guint32)enc.tv_sec) + 1); enc.tv_usec =(long)htonl(ntohl((guint32)enc.tv_usec) + 1); encrypt_data(&enc, SIZEOF(enc), &kh->session_key); if (knet_write(fd, &enc, SIZEOF(enc)) < 0) { security_stream_seterror(&ks->secstr, _("krb4 stream handshake write error: %s"), strerror(errno)); return (-1); } /* * Read back the other side's processing of our data. * If they incremented it properly, then succeed. * Timeout in 10 seconds. */ if (knet_read(fd, &enc, SIZEOF(enc), 60) < 0) { security_stream_seterror(&ks->secstr, _("krb4 stream handshake read error: %s"), strerror(errno)); return (-1); } decrypt_data(&enc, SIZEOF(enc), &kh->session_key); if ((ntohl((guint32)enc.tv_sec) == (uint32_t)(local.tv_sec + 1)) && (ntohl((guint32)enc.tv_usec) == (uint32_t)(local.tv_usec + 1))) return (0); security_stream_seterror(&ks->secstr, _("krb4 handshake failed: sent %ld,%ld - recv %ld,%ld"), (long)(local.tv_sec + 1), (long)(local.tv_usec + 1), (long)ntohl((guint32)enc.tv_sec), (long)ntohl((guint32)enc.tv_usec)); return (-1);}/* * Returns the stream id for this stream. This is just the local * port. */static intkrb4_stream_id( void * s){ struct krb4_stream *ks = s; assert(ks != NULL); return (ks->port);}/* * Write a chunk of data to a stream. Blocks until completion. */static intkrb4_stream_write( void * s, const void *buf, size_t size){ struct krb4_stream *ks = s; assert(ks != NULL); if (knet_write(ks->fd, buf, size) < 0) { security_stream_seterror(&ks->secstr, _("write error on stream %d: %s"), ks->fd, strerror(errno)); return (-1); } return (0);}/* * Submit a request to read some data. Calls back with the given * function and arg when completed. */static voidkrb4_stream_read( void * s, void (*fn)(void *, void *, ssize_t), void * arg){ struct krb4_stream *ks = s; assert(ks != NULL); /* * Only one read request can be active per stream. */ if (ks->ev_read != NULL) event_release(ks->ev_read); ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD, stream_read_callback, ks); ks->fn = fn; ks->arg = arg;}/* * Write a chunk of data to a stream. Blocks until completion. */static ssize_tkrb4_stream_read_sync( void * s, void ** buf){ struct krb4_stream *ks = s; (void)buf; /* Quiet unused variable warning */ assert(ks != NULL); if (ks->ev_read != NULL) event_release(ks->ev_read); ks->ev_read = event_register((event_id_t)ks->fd, EV_READFD, stream_read_sync_callback, ks); event_wait(ks->ev_read); return((ssize_t)ks->len);}/* * Callback for krb4_stream_read_sync */static voidstream_read_sync_callback( void * arg){ struct krb4_stream *ks = arg; ssize_t n; assert(ks != NULL); assert(ks->fd != -1); /* * Remove the event first, and then call the callback. * We remove it first because we don't want to get in their * way if they reschedule it. */ krb4_stream_read_cancel(ks); n = read(ks->fd, ks->databuf, sizeof(ks->databuf)); if (n < 0) security_stream_seterror(&ks->secstr, strerror(errno)); ks->len = (int)n;}/* * Cancel a previous stream read request. It's ok if we didn't have a read * scheduled. */static voidkrb4_stream_read_cancel( void * s){ struct krb4_stream *ks = s; assert(ks != NULL); if (ks->ev_read != NULL) { event_release(ks->ev_read); ks->ev_read = NULL; }}/* * Callback for krb4_stream_read */static voidstream_read_callback( void * arg){ struct krb4_stream *ks = arg; ssize_t n; assert(ks != NULL); assert(ks->fd != -1); /* * Remove the event first, and then call the callback. * We remove it first because we don't want to get in their * way if they reschedule it. */ krb4_stream_read_cancel(ks); n = read(ks->fd, ks->databuf, SIZEOF(ks->databuf)); if (n < 0) security_stream_seterror(&ks->secstr, strerror(errno)); (*ks->fn)(ks->arg, ks->databuf, n);}/* * The callback for recvpkt() for the event handler * Determines if this packet is for this security handle, * and does the real callback if so. */static voidrecvpkt_callback( void * cookie){ char handle[32]; struct sockaddr_in6 peer; pkt_t pkt; int sequence; struct krb4_handle *kh; struct hostent *he; void (*fn)(void *, pkt_t *, security_status_t); void *arg; (void)cookie; /* Quiet unused parameter warning */ assert(cookie == NULL); /* * Find the handle that this packet is associated with. We * need to peek at the packet to see what is in it, but we * want to save the actual reading for later. */ dgram_zero(&netfd); if (dgram_recv(&netfd, 0, &peer) < 0) return; if (str2kpkthdr(netfd.cur, &pkt, handle, SIZEOF(handle), &sequence) < 0) return; for (kh = handleq_first(); kh != NULL; kh = handleq_next(kh)) { if (strcmp(kh->proto_handle, handle) == 0 && cmp_sockaddr(&kh->peer, &peer, 0) == 0) { kh->sequence = sequence; /* * We need to cancel the recvpkt request before calling * the callback because the callback may reschedule us. */ fn = kh->fn; arg = kh->arg; krb4_recvpkt_cancel(kh); if (recv_security_ok(kh, &pkt) < 0) (*fn)(arg, NULL, S_ERROR); else (*fn)(arg, &pkt, S_OK); return; } } /* * If we didn't find a handle, then check for a new incoming packet. * If no accept handler was setup, then just return. */ if (accept_fn == NULL) return; he = gethostbyaddr((void *)&peer.sin6_addr, SIZEOF(peer.sin6_addr), AF_INET6); if (he == NULL) return; kh = alloc(SIZEOF(*kh)); security_handleinit(&kh->sech, &krb4_security_driver); inithandle(kh, he, (int)peer.sin6_port, handle); /* * Check the security of the packet. If it is bad, then pass NULL * to the accept function instead of a packet. */ if (recv_security_ok(kh, &pkt) < 0) (*accept_fn)(&kh->sech, NULL); else (*accept_fn)(&kh->sech, &pkt);}/* * This is called when a handle times out before receiving a packet. */static voidrecvpkt_timeout( void * cookie){ struct krb4_handle *kh = cookie; void (*fn)(void *, pkt_t *, security_status_t); void *arg; assert(kh != NULL); assert(kh->ev_timeout != NULL); fn = kh->fn; arg = kh->arg; krb4_recvpkt_cancel(kh); (*fn)(arg, NULL, S_TIMEOUT);}/* * Add a ticket to the message */static intadd_ticket( struct krb4_handle *kh, const pkt_t * pkt, dgram_t * msg){ char inst[INST_SZ]; KTEXT_ST ticket; char *security; int rc; kh->cksum = (long)krb4_cksum(pkt->body);#if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE /* * User requested that all instances be based on the target * hostname. */ strncpy(inst, kh->inst, SIZEOF(inst) - 1);#else /* * User requested a fixed instance. */ strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);#endif inst[SIZEOF(inst) - 1] = '\0'; /* * Get a ticket with the user-defined service and instance, * and using the checksum of the body of the request packet. */ rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPAL, inst, kh->realm, kh->cksum); if (rc == NO_TKT_FIL) { /* It's been kdestroyed. Get a new one and try again */ get_tgt(); rc = krb_mk_req(&ticket, CLIENT_HOST_PRINCIPAL, inst, kh->realm, kh->cksum); } if (rc != 0) { security_seterror(&kh->sech, _("krb_mk_req failed: %s (%d)"), error_message(rc), rc); return (-1); } /* * We now have the ticket. Put it into the packet, and send * it. */ security = vstralloc("SECURITY TICKET ", bin2astr(ticket.dat, ticket.length), "\n", NULL); dgram_cat(msg, security); amfree(security); return (0);}/* * Add the mutual authenticator. This is the checksum from * the req, + 1 */static voidadd_mutual_auth( struct krb4_handle *kh, dgram_t * msg){ union mutual mutual; char *security; assert(kh != NULL); assert(msg != NULL); assert(kh->cksum != 0); assert(kh->session_key[0] != '\0'); memset(&mutual, 0, SIZEOF(mutual)); mutual.cksum = (unsigned long)htonl((guint32)kh->cksum + 1); encrypt_data(&mutual, SIZEOF(mutual), &kh->session_key); security = vstralloc("SECURITY MUTUAL-AUTH ", bin2astr((unsigned char *)mutual.pad, (int)sizeof(mutual.pad)), "\n", NULL); dgram_cat(msg, security); amfree(security);}/* * Check the security of a received packet. Returns negative on error * or security violation and otherwise returns 0 and fills in the * passed packet. */static intrecv_security_ok( struct krb4_handle *kh, pkt_t * pkt){ char *tok, *security, *body; unsigned long cksum; assert(kh != NULL); assert(pkt != NULL); /* * Set this preemptively before we mangle the body. */ security_seterror(&kh->sech, _("bad %s SECURITY line from %s: '%s'"), pkt_type2str(pkt->type), kh->hostname, pkt->body); /* * The first part of the body should be the security info. Deal with it. * We only need to do this on a few packet types. * * First, parse the SECURITY line in the packet, if it exists. * Increment the cur pointer past it to the data section after * parsing is finished. */ if (strncmp(pkt->body, "SECURITY", SIZEOF("SECURITY") - 1) == 0) { tok = strtok(pkt->body, " "); assert(strcmp(tok, "SECURITY") == 0); /* security info goes until the newline */ security = strtok(NULL, "\n"); body = strtok(NULL, ""); /* * If the body is f-ked, then try to recover. */ if (body == NULL) { if (security != NULL) body = security + strlen(security) + 2; else body = pkt->body; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -