9b00db7f29
* Changes grab to return *ScanResults. Implements ippInContentType correctly. * Slots in an operational re-working of several HTTP module functions, and adds dependency on zgrab's http module. Includes some laregly copy-pasted sections worthy of scrutiny. * Adds support to retry failed HTTP over HTTPS. Removes vestigial functions. * Implements sending CUPS-get-printers request if CUPS is detected, yielding more detailed & accurate version information. Also handles URI's more correctly. * Creates separate container to run IPP over TLS on CUPS. Runs basic tests against both containers. * Creates virtual printer on each container to test for augmenting data with CUPS-get-printers request (which only works when printers exist). * Augments version information with CUPS-get-printers response if possible. * Allows specifying IPP version in constructed requests. Checks for version-not-supported server error. * Allows resending IPP requests with different versions if we hit a version-not-supported error. * Updates IPP zgrab2 schema to include fields added in modules/ipp/scanner.go * Removes unnecessary TODO's * Updates testable example for new definition of AttributeByteString * Removes versionNotSupported's dependency on bufferFromBody. Checks bounds on generated requests' fields correctly. * Updates zgrab2 IPP schema to match ScanResults object in modules/ipp/scanner.go * Corrects IPP tests, bounds checking, zgrab schema formatting. * Logs errors for unexpected behavior in buffer io operations. Updates schema to include standalone fields for attributes described in CUPS-get-printers response. * Logs at debug level only when verbose flag is set. Prints accurate error message when CUPSVersion test fails. * Handles HTTP request errors before checking for nil response/body. Fixes and tests convertURIToIPP.
136 lines
4.8 KiB
Go
136 lines
4.8 KiB
Go
package ipp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"math"
|
|
"strings"
|
|
"net/url"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Writes an "attribute-with-one-value" with the provided "value-tag", "name", and "value" to provided buffer
|
|
// attribute-with-one-value encoding described at https://tools.ietf.org/html/rfc8010#section-3.1.4
|
|
// Example (runnable from ipp_test.go):
|
|
// Input: 0x47, "attributes-charset", "us-ascii"
|
|
// Output: [71 0 18 97 116 116 114 105 98 117 116 101 115 45 99 104 97 114 115 101 116 0 8 117 115 45 97 115 99 105 105]
|
|
// TODO: Switch output and Example function to use hex.Dump()
|
|
// TODO: Should return an error when fed an invalid valueTag
|
|
func AttributeByteString(valueTag byte, name string, value string, target *bytes.Buffer) error {
|
|
//special byte denoting value syntax
|
|
binary.Write(target, binary.BigEndian, valueTag)
|
|
|
|
if len(name) <= math.MaxInt16 && len(name) >= 0 {
|
|
//append 16-bit signed int denoting name length
|
|
binary.Write(target, binary.BigEndian, int16(len(name)))
|
|
|
|
//append name
|
|
binary.Write(target, binary.BigEndian, []byte(name))
|
|
} else {
|
|
// TODO: Log error somewhere
|
|
return errors.New("Name wrong length to encode.")
|
|
}
|
|
|
|
if len(value) <= math.MaxInt16 && len(value) >= 0 {
|
|
//append 16-bit signed int denoting value length
|
|
binary.Write(target, binary.BigEndian, int16(len(value)))
|
|
|
|
//append value
|
|
binary.Write(target, binary.BigEndian, []byte(value))
|
|
} else {
|
|
// TODO: Log error somewhere
|
|
return errors.New("Value wrong length to encode.")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TODO: Eventually handle scheme-less urls, even though getHTTPURL will never construct one (we can use regex)
|
|
// TODO: RFC claims that literal IP addresses are not valid IPP uri's, but Wireshark IPP Capture example uses them
|
|
// (Source: https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=view&target=ipp.pcap)
|
|
func ConvertURIToIPP(uriString string, tls bool) string {
|
|
uri, err := url.Parse(uriString)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"error": err,
|
|
"url": uriString,
|
|
}).Debug("Failed to parse URL from string")
|
|
}
|
|
// TODO: Create a better condition than uri.Scheme == "" b/c url.Parse doesn't know whether there's a scheme
|
|
if uri.Scheme == "" || uri.Scheme == "http" || uri.Scheme == "https" {
|
|
if tls {
|
|
uri.Scheme = "ipps"
|
|
} else {
|
|
uri.Scheme = "ipp"
|
|
}
|
|
}
|
|
if !strings.Contains(uri.Host, ":") {
|
|
uri.Host += ":631"
|
|
}
|
|
return uri.String()
|
|
}
|
|
|
|
func getPrintersRequest(major, minor int8) *bytes.Buffer {
|
|
var b bytes.Buffer
|
|
// Sending too new a version leads to a version-not-supported error, so we'll just send newest
|
|
//version
|
|
b.Write([]byte{byte(major), byte(minor)})
|
|
//operation-id = get-printer-attributes
|
|
b.Write([]byte{0x40, 2})
|
|
//request-id = 1
|
|
b.Write([]byte{0, 0, 0, 1})
|
|
//operation-attributes-tag = 1 (begins an attribute-group)
|
|
b.Write([]byte{1})
|
|
|
|
// TODO: Handle error ocurring in any AttributeByteString call
|
|
//attributes-charset
|
|
AttributeByteString(0x47, "attributes-charset", "utf-8", &b)
|
|
//attributes-natural-language
|
|
AttributeByteString(0x48, "attributes-natural-language", "en-us", &b)
|
|
|
|
//end-of-attributes-tag = 3
|
|
b.Write([]byte{3})
|
|
|
|
return &b
|
|
}
|
|
|
|
// TODO: Store everything except uri statically?
|
|
// Construct a minimal request that an IPP server will respond to
|
|
// IPP request encoding described at https://tools.ietf.org/html/rfc8010#section-3.1.1
|
|
func getPrinterAttributesRequest(major, minor int8, uri string, tls bool) *bytes.Buffer {
|
|
var b bytes.Buffer
|
|
// Using newest version number, because we must provide a supported major version number
|
|
// Object must reply to unsupported major version with
|
|
// "'server-error-version-not-supported' along with the closest version number that
|
|
// is supported" RFC 8011 4.1.8 https://tools.ietf.org/html/rfc8011#4.1.8
|
|
// "In all cases, the IPP object MUST return the "version-number" value that it supports
|
|
// that is closest to the version number supplied by the Client in the request."
|
|
// CUPS behavior defies the RFC. The response to a request with a bad version number should encode
|
|
// the closest supported version number per RFC 8011 Section Appendix B.1.5.4 https://tools.ietf.org/html/rfc8011#appendix-B.1.5.4
|
|
//version
|
|
b.Write([]byte{byte(major), byte(minor)})
|
|
//operation-id = get-printer-attributes
|
|
b.Write([]byte{0, 0xb})
|
|
//request-id = 1
|
|
b.Write([]byte{0, 0, 0, 1})
|
|
//operation-attributes-tag = 1 (begins an attribute-group)
|
|
b.Write([]byte{1})
|
|
|
|
// TODO: Handle error ocurring in any AttributeByteString call
|
|
//attributes-charset
|
|
AttributeByteString(0x47, "attributes-charset", "utf-8", &b)
|
|
//attributes-natural-language
|
|
AttributeByteString(0x48, "attributes-natural-language", "en-us", &b)
|
|
//printer-uri
|
|
AttributeByteString(0x45, "printer-uri", ConvertURIToIPP(uri, tls), &b)
|
|
//requested-attributes
|
|
AttributeByteString(0x44, "requested-attributes", "all", &b)
|
|
|
|
//end-of-attributes-tag = 3
|
|
b.Write([]byte{3})
|
|
|
|
return &b
|
|
}
|