Ejemplo n.º 1
0
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 *)&notice->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, &notice->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 *)&notice->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(&notice->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(&notice->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(&notice->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, &notice->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);
}
Ejemplo n.º 2
0
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
}