Code_t ZParseNotice(char *buffer, int len, ZNotice_t *notice) { char *ptr, *end; unsigned long temp; int maj, numfields, i; #ifndef __LINE__ #define __LINE__ -1 #endif #define BAD_PACKET(what) return _bad_packet(__LINE__, ptr, notice, what) (void) memset((char *)notice, 0, sizeof(ZNotice_t)); ptr = buffer; end = buffer+len; notice->z_packet = buffer; notice->z_version = ptr; if (strncmp(ptr, ZVERSIONHDR, sizeof(ZVERSIONHDR) - 1)) return (ZERR_VERS); ptr += sizeof(ZVERSIONHDR) - 1; if (!*ptr) BAD_PACKET("null version string"); maj = atoi(ptr); if (maj != ZVERSIONMAJOR) return (ZERR_VERS); ptr = next_field(ptr, end); if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) BAD_PACKET("parsing num_hdr_fields"); numfields = temp; notice->z_num_hdr_fields = numfields; ptr = next_field(ptr, end); /*XXX 3 */ numfields -= 2; /* numfields, version, and checksum */ if (numfields < 0) BAD_PACKET("no header fields"); if (numfields && ptr < end) { if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) BAD_PACKET("parsing kind"); notice->z_kind = (ZNotice_Kind_t)temp; numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing kind"); if (numfields && ptr < end) { if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_uid, sizeof(ZUnique_Id_t)) == ZERR_BADFIELD) BAD_PACKET("parsing uid"); notice->z_time.tv_sec = ntohl((u_long) notice->z_uid.tv.tv_sec); notice->z_time.tv_usec = ntohl((u_long) notice->z_uid.tv.tv_usec); numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing uid"); if (numfields && ptr < end) { if (ZReadAscii16(ptr, end-ptr, ¬ice->z_port) == ZERR_BADFIELD) BAD_PACKET("parsing port"); notice->z_port = htons(notice->z_port); numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing port"); if (numfields && ptr < end) { if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) BAD_PACKET("parsing auth"); notice->z_auth = temp; numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing auth"); notice->z_checked_auth = ZAUTH_UNSET; if (numfields && ptr < end) { if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) BAD_PACKET("parsing authenticator length"); notice->z_authent_len = temp; numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing authenticator length"); if (numfields && ptr < end) { notice->z_ascii_authent = ptr; numfields--; ptr = next_field(ptr, end); } else BAD_PACKET("missing authenticator field"); if (numfields && ptr < end) { notice->z_class = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_class = ""; if (numfields && ptr < end) { notice->z_class_inst = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_class_inst = ""; if (numfields && ptr < end) { notice->z_opcode = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_opcode = ""; if (numfields && ptr < end) { notice->z_sender = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_sender = ""; if (numfields && ptr < end) { notice->z_recipient = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_recipient = ""; if (numfields && ptr < end) { notice->z_default_format = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_default_format = ""; if (numfields && ptr < end) { notice->z_ascii_checksum = ptr; if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) notice->z_checksum = 0; else notice->z_checksum = temp; numfields--; ptr = next_field (ptr, end); } else { notice->z_ascii_checksum = ""; notice->z_checksum = 0; } if (numfields && ptr < end) { notice->z_multinotice = ptr; numfields--; ptr = next_field(ptr, end); } else notice->z_multinotice = ""; if (numfields && ptr < end) { if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_multiuid, sizeof(ZUnique_Id_t)) == ZERR_BADFIELD) BAD_PACKET("parsing multiuid"); notice->z_time.tv_sec = ntohl((u_long) notice->z_multiuid.tv.tv_sec); notice->z_time.tv_usec = ntohl((u_long) notice->z_multiuid.tv.tv_usec); numfields--; ptr = next_field(ptr, end); } else notice->z_multiuid = notice->z_uid; if (numfields && ptr < end) { /* we will take it on faith that ipv6 addresses are longer than ipv4 addresses */ unsigned char addrbuf[sizeof(notice->z_sender_sockaddr.ip6.sin6_addr)]; int alen; /* because we're paranoid about naughtily misformatted packets */ if (memchr(ptr, '\0', end - ptr) == NULL) BAD_PACKET("unterminated address field"); if (*ptr == 'Z') { if (ZReadZcode((unsigned char *)ptr, addrbuf, sizeof(addrbuf), &alen) == ZERR_BADFIELD) BAD_PACKET("parsing Zcode address"); } else { alen = sizeof(notice->z_sender_sockaddr.ip4.sin_addr); if (ZReadAscii(ptr, end - ptr, (unsigned char *)addrbuf, alen) == ZERR_BADFIELD) BAD_PACKET("parsing NetASCII address"); } if (alen == sizeof(notice->z_sender_sockaddr.ip6.sin6_addr)) { notice->z_sender_sockaddr.ip6.sin6_family = AF_INET6; memcpy(¬ice->z_sender_sockaddr.ip6.sin6_addr, addrbuf, alen); #ifdef HAVE_SOCKADDR_IN6_SIN6_LEN notice->z_sender_sockaddr.ip6.sin6_len = sizeof(notice->z_sender_sockaddr.ip6); #endif } else if (alen == sizeof(notice->z_sender_sockaddr.ip4.sin_addr)) { notice->z_sender_sockaddr.ip4.sin_family = AF_INET; memcpy(¬ice->z_sender_sockaddr.ip4.sin_addr, addrbuf, alen); #ifdef HAVE_SOCKADDR_IN_SIN_LEN notice->z_sender_sockaddr.ip4.sin_len = sizeof(notice->z_sender_sockaddr.ip4); #endif } else BAD_PACKET("address claims to be neither IPv4 or IPv6"); numfields--; ptr = next_field(ptr, end); } else { memset(¬ice->z_sender_sockaddr, 0, sizeof notice->z_sender_sockaddr); notice->z_sender_sockaddr.ip4.sin_family = AF_INET; notice->z_sender_sockaddr.ip4.sin_addr = notice->z_uid.zuid_addr; #ifdef HAVE_SOCKADDR_IN_SIN_LEN notice->z_sender_sockaddr.ip4.sin_len = sizeof(notice->z_sender_sockaddr.ip4); #endif } if (numfields && ptr < end) { if (ZReadAscii16(ptr, end-ptr, ¬ice->z_charset) == ZERR_BADFIELD) BAD_PACKET("parsing charset"); notice->z_charset = htons(notice->z_charset); numfields--; ptr = next_field(ptr, end); } else notice->z_charset = ZCHARSET_UNKNOWN; for (i=0;ptr < end && i<Z_MAXOTHERFIELDS && numfields;i++,numfields--) { notice->z_other_fields[i] = ptr; ptr = next_field(ptr, end); } notice->z_num_other_fields = i; for (i=0;ptr < end && numfields;numfields--) ptr = next_field(ptr, end); if (numfields || *(ptr - 1) != '\0') BAD_PACKET("end of headers"); notice->z_message = (void *)ptr; notice->z_message_len = len-(ptr-buffer); return (ZERR_NONE); }
Code_t ZCheckSrvAuthentication(ZNotice_t *notice, struct sockaddr_in *from, char *realm) { #ifdef HAVE_KRB5 unsigned char *authbuf; krb5_principal princ; krb5_data packet; krb5_ticket *tkt; char *name; krb5_error_code result; krb5_principal server; krb5_keytab keytabid = 0; krb5_auth_context authctx; krb5_keyblock *keyblock; krb5_enctype enctype; krb5_cksumtype cksumtype; krb5_data cksumbuf; int valid; char *cksum0_base, *cksum1_base = NULL, *cksum2_base; char *x; unsigned char *asn1_data, *key_data, *cksum_data; int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0; KRB5_AUTH_CON_FLAGS_TYPE acflags; #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER krb5_authenticator *authenticator; #define KRB5AUTHENT authenticator #else krb5_authenticator authenticator; #define KRB5AUTHENT &authenticator #endif int len; char *sender; char rlmprincipal[MAX_PRINCIPAL_SIZE]; if (!notice->z_auth) return ZAUTH_NO; /* Check for bogus authentication data length. */ if (notice->z_authent_len <= 0) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: bogus authenticator length"); return ZAUTH_FAILED; } #ifdef HAVE_KRB4 if (notice->z_ascii_authent[0] != 'Z' && realm == NULL) return ZCheckAuthentication4(notice, from); #endif len = strlen(notice->z_ascii_authent)+1; authbuf = malloc(len); /* Read in the authentication data. */ if (ZReadZcode((unsigned char *)notice->z_ascii_authent, authbuf, len, &len) == ZERR_BADFIELD) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: ZReadZcode: Improperly formatted field"); return ZAUTH_FAILED; } if (realm == NULL) { sender = notice->z_sender; } else { (void) snprintf(rlmprincipal, MAX_PRINCIPAL_SIZE, "%s/%s@%s", SERVER_SERVICE, SERVER_INSTANCE, realm); sender = rlmprincipal; } packet.length = len; packet.data = (char *)authbuf; result = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &keytabid); if (result) { free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_kt_resolve: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid */ /* Create the auth context */ result = krb5_auth_con_init(Z_krb5_ctx, &authctx); if (result) { krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_init: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid, authctx */ result = krb5_auth_con_getflags(Z_krb5_ctx, authctx, &acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_getflags: %s", error_message(result)); return ZAUTH_FAILED; } acflags &= ~KRB5_AUTH_CONTEXT_DO_TIME; result = krb5_auth_con_setflags(Z_krb5_ctx, authctx, acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_setflags: %s", error_message(result)); return ZAUTH_FAILED; } result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), __Zephyr_realm, SERVER_SERVICE, SERVER_INSTANCE, NULL); if (!result) { result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, keytabid, NULL, &tkt); krb5_free_principal(Z_krb5_ctx, server); } krb5_kt_close(Z_krb5_ctx, keytabid); /* HOLDING: authbuf, authctx */ if (result) { if (result == KRB5KRB_AP_ERR_REPEAT) syslog(LOG_DEBUG, "ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); else syslog(LOG_WARNING,"ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ if (tkt == 0 || !Z_tktprincp(tkt)) { if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No Ticket"); return ZAUTH_FAILED; } princ = Z_tktprinc(tkt); if (princ == 0) { krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No... Ticket?"); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ result = krb5_unparse_name(Z_krb5_ctx, princ, &name); if (result) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_unparse_name failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_ticket(Z_krb5_ctx, tkt); return ZAUTH_FAILED; } krb5_free_ticket(Z_krb5_ctx, tkt); /* HOLDING: authbuf, authctx, name */ if (strcmp(name, sender)) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: name mismatch: '%s' vs '%s'", name, sender); krb5_auth_con_free(Z_krb5_ctx, authctx); #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); return ZAUTH_FAILED; } #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); /* HOLDING: authctx */ /* Get an authenticator so we can get the keyblock */ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx, &authenticator); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getauthenticator failed: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator */ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getkey failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ /* Figure out what checksum type to use */ key_data = Z_keydata(keyblock); key_len = Z_keylen(keyblock); result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype); if (result) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: Z_ExtractEncCksum failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ if (realm == NULL) ZSetSession(keyblock); /* Assemble the things to be checksummed */ /* first part is from start of packet through z_default_format: * - z_version * - z_num_other_fields * - z_kind * - z_uid * - z_port * - z_auth * - z_authent_len * - z_ascii_authent * - z_class * - z_class_inst * - z_opcode * - z_sender * - z_recipient * - z_default_format */ cksum0_base = notice->z_packet; x = notice->z_default_format; cksum0_len = x + strlen(x) + 1 - cksum0_base; /* second part is from z_multinotice through other fields: * - z_multinotice * - z_multiuid * - z_sender_(sock)addr * - z_charset * - z_other_fields[] */ if (notice->z_num_hdr_fields > 15 ) { cksum1_base = notice->z_multinotice; if (notice->z_num_other_fields) x = notice->z_other_fields[notice->z_num_other_fields - 1]; else { /* see also ZCheckRealmAuthentication and lib/ZCkZaut.c:ZCheckZcodeAuthentication */ /* XXXXXXXXXXXXXXXXXXXXXXX */ if (notice->z_num_hdr_fields > 16) x = cksum1_base + strlen(cksum1_base) + 1; /* multinotice */ if (notice->z_num_hdr_fields > 17) x = x + strlen(x) + 1; /* multiuid */ if (notice->z_num_hdr_fields > 18) x = x + strlen(x) + 1; /* sender */ } cksum1_len = x + strlen(x) + 1 - cksum1_base; /* charset / extra field */ } /* last part is the message body */ cksum2_base = notice->z_message; cksum2_len = notice->z_message_len; /*XXX we may wish to ditch this code someday?*/ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && key_len == 8 && (enctype == (krb5_enctype)ENCTYPE_DES_CBC_CRC || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD4 || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD5)) { /* try old-format checksum (covers cksum0 only) */ ZChecksum_t our_checksum; if (realm == NULL) our_checksum = compute_checksum(notice, key_data); else our_checksum = compute_rlm_checksum(notice, key_data); krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); if (our_checksum == notice->z_checksum) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: des quad checksum mismatch"); return ZAUTH_FAILED; } } /* HOLDING: authctx, authenticator */ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; cksumbuf.data = malloc(cksumbuf.length); if (!cksumbuf.data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(cksumbuf.data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data */ cksum_data = (unsigned char *)cksumbuf.data; memcpy(cksum_data, cksum0_base, cksum0_len); if (cksum1_len) memcpy(cksum_data + cksum0_len, cksum1_base, cksum1_len); memcpy(cksum_data + cksum0_len + cksum1_len, cksum2_base, cksum2_len); /* decode zcoded checksum */ /* The encoded form is always longer than the original */ asn1_len = strlen(notice->z_ascii_checksum) + 1; asn1_data = malloc(asn1_len); if (!asn1_data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(cksumbuf.data); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(asn1_data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ result = ZReadZcode((unsigned char *)notice->z_ascii_checksum, asn1_data, asn1_len, &asn1_len); if (result != ZERR_NONE) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(asn1_data); free(cksumbuf.data); syslog(LOG_WARNING, "ZCheckSrvAuthentication: ZReadZcode: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */ valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_CLT_CKSUM, asn1_data, asn1_len); /* XXX compatibility with unreleased interrealm krb5; drop in 3.1 */ if (!valid && realm) valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_SRV_CKSUM, asn1_data, asn1_len); free(asn1_data); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); krb5_free_keyblock(Z_krb5_ctx, keyblock); free(cksumbuf.data); if (valid) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: Z_krb5_verify_cksum: failed"); return ZAUTH_FAILED; } #else return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO; #endif }