Example #1
0
File: upload.go Project: bfix/sid
func InitDocumentHandler(defs UploadDefs) {

	// initialize upload handling parameters
	uploadPath = defs.Path
	treshold = defs.ShareTreshold

	// check for disabled secret sharing scheme
	if treshold > 0 {
		// compute prime: (2^512-1) - SharePrimeOfs
		one := big.NewInt(1)
		ofs := big.NewInt(int64(defs.SharePrimeOfs))
		prime = new(big.Int).Lsh(one, 512)
		prime = new(big.Int).Sub(prime, one)
		prime = new(big.Int).Sub(prime, ofs)

		// open keyring file
		rdr, err := os.Open(defs.Keyring)
		if err != nil {
			// can't read keys -- terminate!
			logger.Printf(logger.ERROR, "[sid.upload] Can't read keyring file '%s' -- terminating!\n", defs.Keyring)
			os.Exit(1)
		}
		defer rdr.Close()

		// read public keys from keyring
		if reviewer, err = openpgp.ReadKeyRing(rdr); err != nil {
			// can't read keys -- terminate!
			logger.Printf(logger.ERROR, "[sid.upload] Failed to process keyring '%s' -- terminating!\n", defs.Keyring)
			os.Exit(1)
		}
	} else {
		logger.Printf(logger.WARN, "[sid.upload] Secret sharing scheme disabled -- uploads will be stored unencrypted!!")
	}
}
Example #2
0
File: net.go Project: bfix/gospel
// RecvData receives data over network connection (stream-oriented).
func RecvData(conn net.Conn, data []byte, srv string) (int, bool) {

	for retry := 0; retry < retries; {
		// set timeout
		conn.SetDeadline(time.Now().Add(timeout))
		// read data from socket buffer
		n, err := conn.Read(data)
		if err != nil {
			// handle error condition
			switch err.(type) {
			case net.Error:
				// network error: retry...
				nerr := err.(net.Error)
				if nerr.Timeout() {
					return 0, true
				} else if nerr.Temporary() {
					retry++
					time.Sleep(delay)
					continue
				}
			default:
				logger.Printf(logger.INFO, "[%s] Connection closed by peer\n", srv)
				return 0, false
			}
		}
		// report success
		if retry > 0 {
			logger.Printf(logger.INFO, "[%s] %d retries needed to receive data.\n", srv, retry)
		}
		return n, true
	}
	// retries failed
	logger.Printf(logger.ERROR, "[%s] Read failed after retries...\n", srv)
	return 0, false
}
Example #3
0
// RunService runs a TCP/UDP network service with user-defined
// session handler.
func RunService(network, addr string, hdlr []Service) error {

	// initialize control service
	service, err := net.Listen(network, addr)
	if err != nil {
		logger.Println(logger.ERROR, "[network] service start-up failed for '"+network+"/"+addr+"': "+err.Error())
		return err
	}

	// handle connection requests
	go func() {
		for {
			// wait for connection request
			client, err := service.Accept()
			if err != nil {
				logger.Println(logger.ERROR, "[network] accept failed for '"+network+"/"+addr+"': "+err.Error())
				continue
			}
			// find service interface that can handle the request
			accepted := false
			for _, srv := range hdlr {
				// check if connection is allowed:
				remote := client.RemoteAddr().String()
				protocol := client.RemoteAddr().Network()
				// check for matching protocol
				if !srv.CanHandle(protocol) {
					logger.Printf(logger.WARN, "["+srv.GetName()+"] rejected connection protocol '%s' from %s\n", protocol, remote)
					continue
				}
				// check for matching remote address
				if !srv.IsAllowed(remote) {
					logger.Printf(logger.WARN, "["+srv.GetName()+"] rejected connection from %s\n", remote)
					continue
				}
				// connection accepted
				logger.Printf(logger.INFO, "["+srv.GetName()+"] accepted connection from %s\n", remote)
				accepted = true

				// start handler
				go srv.Process(client)
				break
			}
			// close unhandled connections
			if !accepted {
				client.Close()
			}
		}
	}()

	// report success
	logger.Println(logger.INFO, "[network] service started on '"+network+"/"+addr+"'...")
	return nil
}
Example #4
0
File: cover.go Project: bfix/sid
/*
 * Assemble a HTML header from the current state if there are header
 * links we need to reproduce.
 * @param tags *TagList - header tags
 * @param size int - max size of response
 * @return string - assembled header
 */
func (c *Cover) assembleHeader(tags *TagList, size int) string {

	// add header resources
	hdr := "<head>\n"
	for tags.Count() > 0 {
		// get next tag
		tag := tags.Get()
		if tag == nil {
			break
		}
		// translate tag for client
		inl := c.translateTag(tag) + "\n"
		// check if we can add the tag?
		if len(inl) < size {
			// yes: add it to response
			hdr += inl
			size -= len(inl)
		} else {
			// no: put it back
			logger.Printf(logger.WARN, "[sid.cover] can't add all header tags: %d are skipped\n", tags.Count()+1)
			break
		}
	}

	// close header
	hdr += "</head>\n"
	return hdr
}
Example #5
0
File: config.go Project: bfix/sid
/*
 * Set target integer to value parsed from string.
 * @param trgt *int - pointer to target instance
 * @param data string - string representation of value
 */
func SetIntValue(trgt *int, data string) {
	if val, err := strconv.Atoi(data); err == nil {
		*trgt = val
	} else {
		logger.Printf(logger.ERROR, "[sid.config] string conversion from '%s' to integer value failed!", data)
	}
}
Example #6
0
File: net.go Project: bfix/gospel
// SendData sends data over network connection (stream-oriented).
func SendData(conn net.Conn, data []byte, srv string) bool {

	count := len(data) // total length of data
	start := 0         // start position of slice
	retry := 0         // retry counter

	// write data to socket buffer
	for count > 0 {
		// set timeout
		conn.SetDeadline(time.Now().Add(timeout))
		// get (next) chunk to be send
		chunk := data[start : start+count]
		if num, err := conn.Write(chunk); num > 0 {
			// advance slice on partial write
			start += num
			count -= num
			retry = 0
		} else if err != nil {
			// handle error condition
			switch err.(type) {
			case net.Error:
				// network error: retry...
				nerr := err.(net.Error)
				if nerr.Timeout() || nerr.Temporary() {
					retry++
					time.Sleep(delay)
					if retry == retries {
						logger.Printf(logger.ERROR, "[%s] Write failed after retries: %s\n", srv, err.Error())
						return false
					}
				}
			default:
				logger.Printf(logger.INFO, "[%s] Connection closed by peer\n", srv)
				return false
			}
		}
	}
	// report success
	if retry > 0 {
		logger.Printf(logger.INFO, "[%s] %d retries needed to send data.\n", srv, retry)
	}
	return true
}
Example #7
0
File: cover.go Project: bfix/sid
/*
 * Translate tag source attribute: if the source specification is an
 * URI of the form "<scheme>://<server>/<path>/<to>/<resource...>" it
 * is transformed to an absolute path on on the sending server (that is
 * the SID instance) that can later be translated back to its original
 * form; it looks like "/&<scheme>/<server>/<path>/<to>/<resource...>"
 * @param tag *Tag - tag to be translated
 * @return string - translated tag
 */
func (c *Cover) translateTag(tag *Tag) string {

	if src, ok := tag.attrs["src"]; ok {
		// translate "src" attribute of tag
		trgt := translateURI(src)
		logger.Printf(logger.INFO, "[sid.cover] URI translation of '%s' => '%s'\n", src, trgt)
		tag.attrs["src"] = trgt
	} else if src, ok := tag.attrs["href"]; ok {
		// translate "href" attribute of tag
		trgt := translateURI(src)
		logger.Printf(logger.INFO, "[sid.cover] URI translation of '%s' => '%s'\n", src, trgt)
		tag.attrs["href"] = trgt
	} else {
		// failed to access reference attribute?!
		s := tag.String()
		logger.Println(logger.ERROR, "[sid.cover] Tag translation failed: "+s)
		return s
	}
	// return tag representation
	return tag.String()
}
Example #8
0
File: config.go Project: bfix/sid
/*
 * Handle callback from parser.
 * @param mode int - parameter mode
 * @param param *Parameter - reference to new parameter
 * @return bool - successful operation?
 */
func callback(mode int, param *parser.Parameter) bool {
	// if parameter is specified
	if param != nil {

		// print incoming parameter
		logger.Printf(logger.DBG, "[sid.config] %d: `%s=%s`\n", mode, param.Name, param.Value)

		if mode != parser.LIST {
			switch param.Name {
			case "LogFile":
				CfgData.LogFile = param.Value
			case "LogToFile":
				CfgData.LogState = (param.Value == "ON")
			case "LogLevel":
				logger.SetLogLevelFromName(param.Value)
			case "CrtlPort":
				SetIntValue(&CfgData.CtrlPort, param.Value)
			case "CrtlAllow":
				CfgData.CtrlAllow = param.Value
			case "HttpPort":
				SetIntValue(&CfgData.HttpPort, param.Value)
			case "HttpAllow":
				CfgData.HttpAllow = param.Value
			case "UseSocks":
				CfgData.UseSocks = (param.Value == "ON")
			case "SocksAddr":
				CfgData.SocksAddr = param.Value
			case "Path":
				CfgData.Upload.Path = param.Value
			case "Keyring":
				CfgData.Upload.Keyring = param.Value
			case "SharePrimeOfs":
				SetIntValue(&CfgData.Upload.SharePrimeOfs, param.Value)
			case "ShareTreshold":
				SetIntValue(&CfgData.Upload.ShareTreshold, param.Value)
			default:
				if CustomConfigHandler != nil {
					return CustomConfigHandler(mode, param)
				}
			}
		} else {
			if CustomConfigHandler != nil {
				return CustomConfigHandler(mode, param)
			}
		}
	}
	return true
}
Example #9
0
File: cover.go Project: bfix/sid
/*
 * Assemble a HTML body from the current state (like response header),
 * the resource list and a replacement body (addressed by the requested
 * resource path from state).
 * @param s *state - current state info
 * @param size int - target size of response
 * @param done bool - can we close the HTML
 * @return string - assembled HTML body
 */
func (c *Cover) assembleBody(s *State, size int, done bool) string {

	// check if requested size can hold HTML wrapper at all.
	if size < 10 {
		return ""
	}
	// continue HTML body
	resp := ""

	// emit pending reponse data first
	pending := len(s.RespPending)
	logger.Printf(logger.DBG_ALL, "[sid.cover] assembleBody (%d) -- %d\n", size, pending)
	switch {
	case pending > size:
		resp = string(s.RespPending[0:size])
		s.RespPending = string(s.RespPending[size:])
		return resp
	case pending > 0:
		resp = s.RespPending
		size -= pending
		s.RespPending = ""
	}

	// add resources (if any are pending)
	for s.RespTags.Count() > 0 {
		// get next tag
		tag := s.RespTags.Get()
		if tag == nil {
			break
		}
		// translate tag for client
		inl := c.translateTag(tag)
		// check if we can add the tag?
		if len(inl) < size {
			// yes: add it to response
			resp += inl
			size -= len(inl)
		} else {
			// no: put it back
			s.RespTags.Put(tag)
			break
		}
	}
	return resp
}
Example #10
0
/*
 * Initialize image handler: read image definitions from the file
 * specified by the "defs" argument.
 * @param defs string - name of XML-based image definitions
 */
func InitImageHandler() {

	// prepare parsing of image references
	imgList = make([]*ImageRef, 0)
	rdr, err := os.Open(Cfg.ImageDefs)
	if err != nil {
		// terminate application in case of failure
		logger.Println(logger.ERROR, "[images] Can't read image definitions -- terminating!")
		logger.Println(logger.ERROR, "[images] "+err.Error())
		os.Exit(1)
	}
	defer rdr.Close()

	// parse XML file and build image reference list
	decoder := xml.NewDecoder(rdr)
	var list ImageList
	if err = decoder.Decode(&list); err != nil {
		// terminate application in case of failure
		logger.Println(logger.ERROR, "[images] Can't decode image definitions -- terminating!")
		logger.Println(logger.ERROR, "[images] "+err.Error())
		os.Exit(1)
	}
	for _, img := range list.Image {
		logger.Println(logger.DBG, "[images]: image="+img.Name)
		// get size of image file
		fi, err := os.Stat(img.Path)
		if err != nil {
			logger.Println(logger.ERROR, "[images] image '"+img.Path+"' missing!")
			continue
		}
		// clone to reference instance
		ir := &ImageRef{
			name:    img.Name,
			comment: img.Comment,
			path:    img.Path,
			mime:    img.Mime,
			size:    int(fi.Size()),
		}
		// add to image list
		imgList = append(imgList, ir)
	}
	logger.Printf(logger.INFO, "[images] %d images available\n", len(imgList))
}
Example #11
0
File: html.go Project: bfix/sid
/*
 * Get list of attributes for a tag.
 * If the tag is at the end of a HTML fragment and not all attributes
 * can be read by the tokenizer, this call terminates with a "nil"
 * map to indicate failure. The tag is than dropped (for an eavesdropper
 * this looks like a cached resource)
 * @param tk *html.Tokenizer - tokenizer instance
 * @return map[string]string - list of attributes
 */
func getAttrs(tk *html.Tokenizer) (list map[string]string) {

	// handle panic during parsing
	defer func() {
		if r := recover(); r != nil {
			logger.Printf(logger.WARN, "[sid.html] Skipping fragmented tag: %v\n", r)
			list = nil
		}
	}()

	// parse attributes from HTML text
	list = make(map[string]string)
	for {
		key, val, more := tk.TagAttr()
		list[string(key)] = string(val)
		if !more {
			break
		}
	}
	return
}
Example #12
0
File: cover.go Project: bfix/sid
/*
 * Transform cover server response: Substitute absolute URLs in the
 * response to local links to be handled by the request translations.
 * @param s *state - reference to state information
 * @param data []byte - response data from cover server
 * @param num int - length of response data
 * @return []data - transformed response (send to client)
 */
func (c *Cover) xformResp(s *State, data []byte, num int) []byte {

	// log incoming packet
	inStr := string(data[0:num])
	logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes received from cover server.\n", num)
	logger.Println(logger.DBG_ALL, "[sid.cover] Incoming response:\n"+inStr+"\n")

	// setup reader and response
	size := num
	rdr := bytes.NewBuffer(data[0:num])
	resp := ""

	// initial response package
	if s.RespMode == 0 {

		// use identical line break sequence
		lb := "\r\n"
		if strings.Index(inStr, lb) == -1 {
			lb = "\n"
		}
		// start of new response encountered: parse header fields
	hdr:
		for {
			// get next line (terminated by line break)
			line, err := rdr.ReadString('\n')
			line = strings.TrimRight(line, "\n\r")
			if err != nil {
				// header is not complete: wait for next response fragment
				logger.Println(logger.WARN, "[sid.cover] Response header fragmented!")
				logger.Println(logger.DBG, "[sid.cover] Assembled response:\n"+resp)
				if size != len(resp) {
					logger.Printf(logger.WARN, "[sid.cover] DIFF(response:1) = %d\n", len(resp)-size)
				}
				return []byte(resp)
			}
			// check if header is available at all..
			if strings.HasPrefix(line, "<!") {
				logger.Println(logger.INFO, "[sid.cover] No response header found: "+line)
				break hdr
			}

			// parse response header
			switch {
			//-----------------------------------------------------
			// Header parsing complete
			//-----------------------------------------------------
			case len(line) == 0:
				// we have parsed the header; continue with body
				logger.Println(logger.DBG_ALL, "[sid.cover] Incoming response header:\n"+resp)
				// drop length encoding on gzip content
				break hdr

			//-----------------------------------------------------
			// Status line
			//-----------------------------------------------------
			case strings.HasPrefix(line, "HTTP/"):
				// split line into parts
				parts := strings.Split(line, " ")
				status, _ := strconv.Atoi(parts[1])
				logger.Printf(logger.DBG, "[sid.cover] response status: %d\n", status)
				if status != 200 {
					return data[:size]
				}

			//-----------------------------------------------------
			// Content-Type:
			//-----------------------------------------------------
			case strings.HasPrefix(line, "Content-Type: "):
				// split line into parts
				parts := strings.Split(line, " ")
				s.RespType = strings.TrimRight(parts[1], ";")
				logger.Println(logger.DBG_HIGH, "[sid.cover] response type: "+s.RespType)

			//-----------------------------------------------------
			// Content-Encoding:
			//-----------------------------------------------------
			case strings.HasPrefix(line, "Content-Encoding: "):
				// split line into parts
				parts := strings.Split(line, " ")
				s.RespEnc = parts[1]
				logger.Println(logger.DBG_HIGH, "[sid.cover] response encoding: "+s.RespEnc)

			//-----------------------------------------------------
			// location:
			//-----------------------------------------------------
			case strings.HasPrefix(line, "location: "):
				// split line into parts
				parts := strings.Split(line, " ")
				line = "location: " + translateURI(parts[1])
				logger.Println(logger.DBG_HIGH, "[sid.cover] changing location => "+line)
			}
			// assemble response
			resp += line + lb
		}
		// add delimiter line
		resp += lb
		// adjust remaining content size
		num -= len(resp)
	}

	// start of HTML response?
	if s.RespMode == 0 {
		//-------------------------------------------------------------
		// start HTML response
		//-------------------------------------------------------------
		if strings.HasPrefix(s.RespType, "text/html") {
			// start of a new HTML response. Use pre-defined HTML page
			// to initialize response.
			var coverId string = ""
			s.RespPending, coverId = c.HandleRequest(c, s)
			s.Data["CoverId"] = coverId
		}
		// switch to next mode
		s.RespMode = 1
	}

	switch {
	//-------------------------------------------------------------
	// assemble HTML response
	//-------------------------------------------------------------
	case strings.HasPrefix(s.RespType, "text/html"):
		// do content translation (collect resource tags)
		done := parseHTML(rdr, s.RespHdr, s.RespTags, s.RespXtra)
		// sync replacement body (cover content) if response has
		// been completely processed.
		if done {
			c.SyncCover(c, s)
		}

		// start of HTML?
		if s.RespMode == 1 {
			// initial HTML sequence
			resp += htmlIntro
			// output header if available
			if s.RespHdr.Count() > 0 {
				hdr := c.assembleHeader(s.RespHdr, num)
				resp += hdr
				num -= len(hdr)
			}
			// handle HTML body
			s.RespMode = 2
			// open body tag
			str := "<body>\n"
			resp += str
			num -= len(str)
		}
		// continue to assemble HTML body
		body := c.assembleBody(s, num, done)
		resp += body
		num -= len(body)
		if done {
			// close body and HTML
			resp += htmlOutro
			num -= len(htmlOutro)
		}
		// we are done with this response packer, but have still response
		// data to transfer. Fill up with padding sequence.
		resp += padding(num)

		// return response data
		if size != len(resp) {
			logger.Printf(logger.WARN, "[sid.cover] DIFF(response:2) = %d\n", len(resp)-size)
		}
		logger.Println(logger.DBG_ALL, "[sid.cover] Translated response:\n"+resp)
		return []byte(resp)

	//-------------------------------------------------------------
	// Images: Images are considered harmless, so we simply
	// pass them back to the client.
	//-------------------------------------------------------------
	case strings.HasPrefix(s.RespType, "image/"):
		logger.Println(logger.DBG, "[sid.cover] Image data passed to client")
		return data[0:size]

	//-------------------------------------------------------------
	// JavaScript: Simply replace any JavaScript content with
	// spaces (looks like the client browser has disabled
	// JavaScript).
	//-------------------------------------------------------------
	case strings.HasPrefix(s.RespType, "application/x-javascript"):
		// padding to requested size
		for n := 0; n < num; n++ {
			resp += " "
		}
		// return response data
		logger.Println(logger.DBG, "[sid.cover] JavaScript scrubbed")
		if size != len(resp) {
			logger.Printf(logger.WARN, "[sid.cover] DIFF(response:3) = %d\n", len(resp)-size)
		}
		return []byte(resp)

	//-------------------------------------------------------------
	// CSS: Simply replace any style sheets with spaces. No image
	// references in CSS are parsed (looks like those are cached
	// resources to an eavesdropper)
	//-------------------------------------------------------------
	case strings.HasPrefix(s.RespType, "text/css"):
		// padding to requested size
		for n := 0; n < num; n++ {
			resp += " "
		}
		// return response data
		logger.Println(logger.DBG, "[sid.cover] CSS scrubbed")
		if size != len(resp) {
			logger.Printf(logger.WARN, "[sid.cover] DIFF(response:4) = %d\n", len(resp)-size)
		}
		return []byte(resp)
	}

	//return untranslated response
	logger.Println(logger.ERROR, "[sid.cover] Unhandled response!")
	return data[0:size]
}
Example #13
0
File: cover.go Project: bfix/sid
/*
 * Transform client request: this is supposed to work on fragmented
 * requests if necessary (currently not really supported)
 * @param s *state - reference to state information
 * @param data []byte - request data from client
 * @param num int - length of request in bytes
 * @return []byte - transformed request (send to cover server)
 */
func (c *Cover) xformReq(s *State, data []byte, num int) []byte {

	inStr := string(data[0:num])
	logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes received from client.\n", num)
	logger.Println(logger.DBG_ALL, "[sid.cover] Incoming request:\n"+inStr+"\n")

	// assemble transformed request
	rdr := bufio.NewReader(strings.NewReader(inStr))
	req := ""
	hasContentEncoding := false // expected content encoding defined?
	//hasTransferEncoding := false		// expected transfer encoding defined?
	mime := "text/html"  // expected content type
	targetHost := c.Name // request resource from this host (default)
	balance := 0         // balance between incoming and outgoing information

	// use identical line break sequence
	lb := "\r\n"
	if strings.Index(inStr, lb) == -1 {
		lb = "\n"
	}
	for s.ReqState == RS_HDR {
		// get next line (terminated by line break)
		b, broken, _ := rdr.ReadLine()
		if b == nil || len(b) == 0 {
			if !broken {
				s.ReqState = RS_HDR_COMPLETE
			}
			break
		}
		line := strings.TrimRight(string(b), "\r\n")

		// transform request data
		switch {
		//---------------------------------------------------------
		// POST command: upload document
		// This command triggers the upload of a document to SID
		// that is covered by an upload to the cover site of the
		// same length.
		//---------------------------------------------------------
		case strings.HasPrefix(line, "POST "):
			// split line into parts
			parts := strings.Split(line, " ")
			logger.Printf(logger.DBG_HIGH, "[sid.cover] POST '%s'\n", parts[1])

			// POST uri encodes the key to the cover POST content and the
			// target POST URL
			elem := strings.Split(parts[1], "/")
			s.ReqBoundaryOut = elem[1]
			uri := ""
			for i := 2; i < len(elem); i++ {
				uri += "/" + elem[i]
			}

			// try to get pre-defined cover content. if no cover content
			// has been constructed yet, the 'reqCoverPost' will contain
			// nil and the content is constructed later when the content
			// length of the incoming request is known.
			s.ReqCoverPost = c.GetPostContent(s.ReqBoundaryOut)
			s.ReqCoverPostPos = 0

			// if URI refers to an external host, split into
			// host reference and resource specification
			if pos := strings.Index(uri, "://"); pos != -1 {
				rem := string(uri[pos+3:])
				pos = strings.Index(rem, "/")
				if pos != -1 {
					targetHost = rem[0:pos]
					uri = rem[pos:]
					logger.Printf(logger.INFO, "[sid.cover] URI split: '%s', '%s'\n", targetHost, uri)
				} else {
					logger.Printf(logger.WARN, "[sid.cover] URI split failed on '%s'\n", uri)
				}
			} else {
				targetHost = c.Name
			}

			// assemble new POST request
			s.ReqResource = uri
			req += "POST " + uri + " HTTP/1.0" + lb
			s.ReqMode = REQ_POST

			// keep balance
			balance += (len(parts[1]) - len(uri))

		//---------------------------------------------------------
		// GET command: request resource
		// If the requested resource identifier is a translated
		// entry, we need to translate that back into its original
		// form. Translated entries start with "/&".
		// It is assumed, that a "GET" line is one of the first
		// lines in a request and therefore never fragmented.
		// N.B.: We also force HTTP/1.0 to ensure that no
		// chunking is used by the server (easier parsing).
		//---------------------------------------------------------
		case strings.HasPrefix(line, "GET "):
			// split line into parts
			parts := strings.Split(line, " ")
			logger.Printf(logger.DBG_HIGH, "[sid.cover] resource='%s'\n", parts[1])

			// perform translation (if required)
			uri := translateURI(parts[1])
			logger.Printf(logger.INFO, "[sid.cover] URI translation: '%s' => '%s'\n", parts[1], uri)

			// if URI refers to an external host, split into
			// host reference and resource specification
			if pos := strings.Index(uri, "://"); pos != -1 {
				rem := string(uri[pos+3:])
				pos = strings.Index(rem, "/")
				if pos != -1 {
					targetHost = rem[0:pos]
					uri = rem[pos:]
					logger.Printf(logger.INFO, "[sid.cover] URI split: '%s', '%s'\n", targetHost, uri)
				} else {
					logger.Printf(logger.WARN, "[sid.cover] URI split failed on '%s'\n", uri)
				}
			} else {
				targetHost = c.Name
			}

			// assemble new resource request
			s.ReqResource = uri
			req += "GET " + uri + " HTTP/1.0" + lb
			s.ReqMode = REQ_GET

			// keep balance
			balance += (len(parts[1]) - len(uri))

		//---------------------------------------------------------
		// Host reference: change to hostname of cover server
		// This translation may leed to unbalanced request sizes;
		// the balance will be equalled in a later line
		// It is assumed, that a "Host:" line is one of the first
		// lines in a request and therefore never fragmented.
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Host: "):
			// split line into parts
			parts := strings.Split(line, " ")
			// replace hostname reference
			logger.Printf(logger.DBG_HIGH, "[sid.cover] Host replaced with '%s'\n", targetHost)
			req += "Host: " + targetHost + lb
			// keep track of balance
			balance += (len(parts[1]) - len(targetHost))

		//---------------------------------------------------------
		// try to get balance straight on language header line:
		// "Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3"
		//---------------------------------------------------------
		//case s.ReqBalance != 0 && strings.HasPrefix (line, "Accept-Language: "):
		// @@@TODO: Is this the right place to balance the translation?

		//---------------------------------------------------------
		// Acceptable content encoding: we only want plain HTML
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Accept-Encoding: "):
			// split line into parts
			parts := strings.Split(line, " ")
			hasContentEncoding = true
			if mime == "text/html" && parts[1] != "identity" {
				// change to identity encoding for HTML pages
				repl := "Accept-Encoding: identity"
				balance += len(repl) - len(line)
				req += repl + lb
			} else {
				req += line + lb
			}
			/*
				//---------------------------------------------------------
				// Acceptable transfer encoding: we only want no chunking
				//---------------------------------------------------------
				case strings.HasPrefix (line, "Transfer-Encoding: "):
					// split line into parts
					parts := strings.Split (line, " ")
					hasTransferEncoding = true
					if mime == "text/html" && parts[1] != "identity" {
						// change to identity transfer for HTML pages
						repl := "Transfer-Encoding: identity"
						balance += len(repl) - len(line)
						req += repl + lb
					} else {
						req += line + lb
					}
			*/
		//---------------------------------------------------------
		// Expected content type
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Content-Type: "):
			// split line into parts
			parts := strings.Split(line, " ")
			mime = parts[1]
			// remember boundary definition
			if s.ReqMode == REQ_POST {
				// strip "boundary="
				s.ReqBoundaryIn = string(parts[2][9:])
				logger.Println(logger.DBG_HIGH, "[sid.cover] Boundary="+s.ReqBoundaryIn)
				repl := parts[0] + " " + mime +
					" boundary=---------------------------" + s.ReqBoundaryOut
				balance += len(repl) - len(line)
				req += repl + lb
			} else {
				req += line + lb
			}

		//---------------------------------------------------------
		// Referer
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Referer: "):
			repl := "Referer: " + c.Protocol + "://" + targetHost + "/"
			balance += len(repl) - len(line)
			req += repl + lb

		//---------------------------------------------------------
		// Connection
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Connection: "):
			// split line into parts
			parts := strings.Split(line, " ")
			if parts[1] != "close" {
				repl := "Connection: close"
				balance += len(repl) - len(line)
				req += repl + lb
			} else {
				req += line + lb
			}

		//---------------------------------------------------------
		// Keep-Alive:
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Keep-Alive: "):
			// don't add spec
			balance -= len(line)

		//---------------------------------------------------------
		// Content-Length
		//---------------------------------------------------------
		case strings.HasPrefix(line, "Content-Length: "):
			// do we have a pre-defined cover content?
			if s.ReqCoverPost == nil || s.ReqCoverPost[0] == '!' {
				// split line into parts
				parts := strings.Split(line, " ")
				// get incoming content length
				s.ReqContentLength, _ = strconv.Atoi(parts[1])
				// construct/expand cover content for given size
				s.ReqCoverPost = c.FinalizeCover(c, s)
			}
			// use cover content to construct a content length
			repl := "Content-Length: " + strconv.Itoa(len(s.ReqCoverPost))
			balance += len(repl) - len(line)
			req += repl + lb

		//---------------------------------------------------------
		// add unchanged request lines.
		//---------------------------------------------------------
		default:
			req += line
			if !broken {
				req += lb
			}
		}
	}

	// check for completed header in this pass
	if s.ReqState == RS_HDR_COMPLETE {
		// add delimiting empty line
		req += lb

		// post-process header
		if mime == "text/html" {
			if !hasContentEncoding {
				// enforce identity encoding for HTML pages
				repl := "Accept-Encoding: identity"
				balance += len(repl)
				req += repl + lb
			}
			/*
				if !hasTransferEncoding {
					// enforce identity transfer for HTML pages
					repl := "Transfer-Encoding: identity"
					balance += len(repl)
					req += repl + lb
				}
			*/
		}

		if s.ReqMode == REQ_POST {
			// switch state
			s.ReqState = RS_CONTENT
		} else {
			// we are done
			s.ReqState = RS_DONE
		}
	}

	// handle processing of request contents for POST requests
	if s.ReqState == RS_CONTENT {

		// parse data until end of request
		for {
			// get next line (terminated by line break)
			// and adjust number of bytes read
			b, _, err := rdr.ReadLine()
			if err != nil {
				break
			}
			line := strings.TrimRight(string(b), "\r\n")

			//logger.Println (logger.DBG_ALL, "[sid.cover] POST content: " + line + "\n")

			if !s.ReqUpload {
				// check for start of document
				if strings.Index(line, "name=\"file\";") != -1 {
					s.ReqUpload = true
					s.ReqUploadData = ""
				}
			} else {
				if strings.Index(line, s.ReqBoundaryIn) != -1 {
					s.ReqUpload = false
					s.ReqUploadOK = PostprocessUploadData([]byte(s.ReqUploadData))
				}
				// we are uploading client data
				s.ReqUploadData += line + lb
			}
		}

		// build new request data
		binReq := []byte(req)
		copy(data, binReq)
		pos := len(binReq)
		count := num - pos

		// we have "count" bytes of response data to sent out
		start := s.ReqCoverPostPos
		total := len(s.ReqCoverPost)
		if start < total {
			end := start + count
			s.ReqCoverPostPos = end
			if end > total {
				end = total
			}
			copy(data[pos:], s.ReqCoverPost[start:end])
			pos += (end - start)
		}

		// fill up with line breaks
		if pos < num {
			fill := ""
			for count = num - pos; count > 0; count-- {
				fill += "\n"
			}
			data = append(data[0:pos], []byte(fill)...)
			pos = num
		}

		outStr := string(data[0:pos])
		logger.Printf(logger.DBG_HIGH, "[sid.cover] %d bytes send to cover server.\n", pos)
		logger.Println(logger.DBG_ALL, "[sid.cover] Outgoing request:\n"+outStr+"\n")
		return data[0:pos]
	}

	// check for completed request processing
	if s.ReqState == RS_DONE {
		if balance != 0 {
			logger.Printf(logger.WARN, "[sid.cover] Unbalanced request: %d bytes diff\n", balance)
		}
	} else {
		// padding of request with line breaks (if assembled request is smaller; GET only)
		for num > len(req) && s.ReqMode == REQ_GET {
			req += "\n"
		}
		// return transformed request
		if num != len(req) {
			logger.Printf(logger.WARN, "[sid.cover] DIFF(request) = %d\n", len(req)-num)
		}
		logger.Printf(logger.DBG_ALL, "[sid.cover] Transformed request:\n"+req+"\n")
	}
	return []byte(req)
}
Example #14
0
File: cover.go Project: bfix/sid
/*
 * Connect to cover server
 * @return net.Conn - connection to cover server (or nil)
 */
func (c *Cover) connect() net.Conn {
	// establish connection directly or via proxy.
	var (
		conn net.Conn
		err  error
	)
	if CfgData.UseSocks {
		conn, err = network.Socks5Connect("tcp", c.Name, c.Port, CfgData.SocksAddr)
		if err != nil {
			// can't connect
			logger.Printf(logger.ERROR, "[sid.cover] failed to connect to cover server through SOCKS5 proxy: %s\n", err.Error())
			return nil
		}
		logger.Println(logger.INFO, "[sid.cover] connected to cover server through SOCKS5 proxy...")
	} else {
		addr := c.Name + ":" + strconv.Itoa(c.Port)
		conn, err = net.Dial("tcp", addr)
		if err != nil {
			// can't connect
			logger.Printf(logger.ERROR, "[sid.cover] failed to connect to cover server: %s\n", err.Error())
			return nil
		}
		logger.Println(logger.INFO, "[sid.cover] directly connected to cover server...")
	}

	// allocate state information and add to state list
	// initialize struct with default data
	c.States[conn] = &State{
		//-------------------------------------------------------------
		// Request state
		//-------------------------------------------------------------
		ReqMode:         REQ_UNKNOWN,
		ReqState:        RS_HDR,
		ReqResource:     "",
		ReqBoundaryIn:   "",
		ReqBoundaryOut:  "",
		ReqCoverPost:    nil,
		ReqCoverPostPos: 0,
		ReqUpload:       false,
		ReqUploadOK:     false,
		ReqUploadData:   "",

		//-------------------------------------------------------------
		// Response state
		//-------------------------------------------------------------
		RespPending: "",
		RespEnc:     "",
		RespMode:    0,
		RespSize:    0,
		RespType:    "text/html",
		RespHdr:     NewTagList(),
		RespTags:    NewTagList(),
		RespXtra:    NewTagList(),

		//-------------------------------------------------------------
		// Additional data
		//-------------------------------------------------------------
		Data: make(map[string]string),
	}
	return conn
}
Example #15
0
File: config.go Project: bfix/sid
/*
 * Setup configuration data: Handle SID configuration data and
 * call custom handler for non-standard configuration data
 */
func InitConfig() {

	// process command line arguments
	CfgData.CfgFile = *flag.String("c", CfgData.CfgFile, "configuration file")
	flag.String("L", CfgData.LogFile, "logfile name")
	flag.Bool("l", CfgData.LogState, "file-based logging")
	flag.Int("p", CfgData.CtrlPort, "control session port")
	flag.Int("h", CfgData.HttpPort, "HTTP session port")
	flag.Parse()

	// read configuration from file
	logger.Println(logger.INFO, "[sid.config] using configuration file '"+CfgData.CfgFile+"'")
	cfg, err := os.Open(CfgData.CfgFile)
	if err != nil {
		logger.Println(logger.WARN, "[sid.config] configuration file not available -- using defaults")
		return
	}
	// configuration file exists: read parameters
	rdr := bufio.NewReader(cfg)
	err = parser.Parser(rdr, callback)
	if err != nil {
		logger.Printf(logger.ERROR, "[sid.config] error reading configuration file: %v\n", err)
		os.Exit(1)
	}
	logger.Println(logger.INFO, "[sid.config] configuration file complete.")

	// handle command line flags that may override options specified in the
	// configuration file (or are default values)
	flag.Visit(func(f *flag.Flag) {
		val := f.Value.String()
		logger.Printf(logger.INFO, "[sid.config] Overriding '%s' with '%s'\n", f.Usage, val)
		switch f.Name {
		case "L":
			CfgData.LogFile = val
		case "l":
			CfgData.LogState = (val == "true")
		case "p":
			CfgData.CtrlPort, _ = strconv.Atoi(val)
		case "h":
			CfgData.HttpPort, _ = strconv.Atoi(val)
		}
	})

	// turn on logging if specified on command line or config file
	if CfgData.LogState {
		logger.Println(logger.INFO, "[sid.config] File logging requested.")
		if !logger.LogToFile(CfgData.LogFile) {
			CfgData.LogState = false
		}
	}

	// set networking parameter
	network.Delay = 1000000                          // 1ms
	network.Retries = 1000                           // max. 1s
	network.Timeout, _ = time.ParseDuration("100us") // 0.1ms
	proxy := "<None>"
	if CfgData.UseSocks {
		proxy = CfgData.SocksAddr
	}

	// list current configuration data
	logger.Println(logger.INFO, "[sid.config] !==========< configuration >===============")
	logger.Println(logger.INFO, "[sid.config] !       Configuration file: "+CfgData.CfgFile)
	logger.Println(logger.INFO, "[sid.config] !Port for control sessions: "+strconv.Itoa(CfgData.CtrlPort))
	logger.Println(logger.INFO, "[sid.config] !   Port for HTTP sessions: "+strconv.Itoa(CfgData.HttpPort))
	logger.Println(logger.INFO, "[sid.config] !              SOCKS proxy: "+proxy)
	logger.Println(logger.INFO, "[sid.config] !==========================================")
}
Example #16
0
File: smtp.go Project: bfix/gospel
// ParseEncrypted parses a encrypted (and possibly signed) message.
func ParseEncrypted(ct, addr string, getInfo MailUserInfo, body io.Reader) (*MailContent, error) {
	mc := new(MailContent)
	mc.Mode = modeENC
	boundary := extractValue(ct, "boundary")
	rdr := multipart.NewReader(body, boundary)
	for {
		if part, err := rdr.NextPart(); err == nil {
			ct = part.Header.Get("Content-Type")
			switch {
			case strings.HasPrefix(ct, "application/pgp-encrypted"):
				buf, err := ioutil.ReadAll(part)
				if err != nil {
					return nil, err
				}
				logger.Printf(logger.DBG, "application/pgp-encrypted: '%s'\n", strings.TrimSpace(string(buf)))
				continue
			case strings.HasPrefix(ct, "application/octet-stream;"):
				rdr, err := armor.Decode(part)
				if err != nil {
					return nil, err
				}
				pw := ""
				pwTmp := getInfo(infoPASSPHRASE, "")
				switch pwTmp.(type) {
				case string:
					pw = pwTmp.(string)
				}
				prompt := func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
					priv := keys[0].PrivateKey
					if priv.Encrypted {
						priv.Decrypt([]byte(pw))
					}
					buf := new(bytes.Buffer)
					priv.Serialize(buf)
					return buf.Bytes(), nil
				}
				id := getIdentity(getInfo, infoIDENTITY, "")
				md, err := openpgp.ReadMessage(rdr.Body, openpgp.EntityList{id}, prompt, nil)
				if err != nil {
					return nil, err
				}
				if md.IsSigned {
					mc.Mode = modeSIGNENC
					id := getIdentity(getInfo, infoSENDER, addr)
					if id == nil {
						mc.Mode = modeUSIGNENC
						content, err := ioutil.ReadAll(md.UnverifiedBody)
						if err != nil {
							return nil, err
						}
						mc.Body = string(content)
						continue
					}
					md.SignedBy = crypto.GetKeyFromIdentity(id, crypto.KeySign)
					md.SignedByKeyId = md.SignedBy.PublicKey.KeyId
					mc.Key, err = crypto.GetArmoredPublicKey(id)
					if err != nil {
						return nil, err
					}
					content, err := ioutil.ReadAll(md.UnverifiedBody)
					if err != nil {
						return nil, err
					}
					if md.SignatureError != nil {
						return nil, md.SignatureError
					}
					logger.Println(logger.INFO, "Signature verified OK")

					m, err := mail.ReadMessage(bytes.NewBuffer(content))
					if err != nil {
						return nil, err
					}
					ct = m.Header.Get("Content-Type")
					mc2, err := ParsePlain(ct, m.Body)
					if err != nil {
						return nil, err
					}
					mc.Body = mc2.Body
				}
			default:
				return nil, errors.New("Unhandled MIME part: " + ct)
			}
		} else if err == io.EOF {
			break
		} else {
			return nil, err
		}
	}
	return mc, nil
}
Example #17
0
File: upload.go Project: bfix/sid
/*
 * Client upload data received.
 * @param data []byte - uploaded document data
 * @return bool - post-processing successful?
 */
func PostprocessUploadData(data []byte) bool {

	logger.Println(logger.INFO, "[sid.upload] Client upload received")
	logger.Println(logger.DBG_ALL, "[sid.upload] Client upload data:\n"+string(data))

	baseName := uploadPath + "/" + CreateId(16)

	// check if we use a shared secret scheme
	if reviewer == nil {
		// no: store content unencrypted.
		fname := baseName + ".document"
		wrt, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
		if err != nil {
			logger.Printf(logger.ERROR, "[sid.upload] Can't create document file '%s'\n", fname)
			return false
		}
		// write content and close file
		wrt.Write(data)
		wrt.Close()
	} else {
		// yes: use shared secret scheme to store upload in encrypted form.
		var (
			err    error
			engine cipher.Block   = nil
			wrt    io.WriteCloser = nil
			ct     io.WriteCloser = nil
			pt     io.WriteCloser = nil
		)

		//-----------------------------------------------------------------
		// setup AES-256 for encryption
		//-----------------------------------------------------------------
		key := crypto.RandBytes(32)
		if engine, err = aes.NewCipher(key); err != nil {
			// should not happen at all; epic fail if it does
			logger.Println(logger.ERROR, "[sid.upload] Failed to setup AES cipher!")
			return false
		}
		bs := engine.BlockSize()
		iv := crypto.RandBytes(bs)
		enc := cipher.NewCFBEncrypter(engine, iv)

		logger.Println(logger.DBG_ALL, "[sid.upload] key:\n"+hex.Dump(key))
		logger.Println(logger.DBG_ALL, "[sid.upload] IV:\n"+hex.Dump(iv))

		//-----------------------------------------------------------------
		// encrypt client document into file
		//-----------------------------------------------------------------

		// open file for output
		fname := baseName + ".document.aes256"
		if wrt, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
			logger.Printf(logger.ERROR, "[sid.upload] Can't create document file '%s'\n", fname)
			return false
		}
		// write iv first
		wrt.Write(iv)
		// encrypt binary data for the document
		logger.Println(logger.DBG_ALL, "[sid.upload] AES256 in:\n"+hex.Dump(data))
		enc.XORKeyStream(data, data)
		logger.Println(logger.DBG_ALL, "[sid.upload] AES256 out:\n"+hex.Dump(data))
		// write to file
		wrt.Write(data)
		wrt.Close()

		//-----------------------------------------------------------------
		//	create shares from secret
		//-----------------------------------------------------------------
		secret := new(big.Int).SetBytes(key)
		n := len(reviewer)
		shares := crypto.Split(secret, prime, n, treshold)
		recipient := make([]*openpgp.Entity, 1)

		for i, ent := range reviewer {
			// generate filename based on key id
			id := strconv.FormatUint(ent.PrimaryKey.KeyId&0xFFFFFFFF, 16)
			fname = baseName + "." + strings.ToUpper(id) + ".gpg"
			// create file for output
			if wrt, err = os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
				logger.Printf(logger.ERROR, "[sid.upload] Can't create share file '%s'\n", fname)
				continue
			}
			// create PGP armorer
			if ct, err = armor.Encode(wrt, "PGP MESSAGE", nil); err != nil {
				logger.Printf(logger.ERROR, "[sid.upload] Can't create armorer: %s\n", err.Error())
				wrt.Close()
				continue
			}
			// encrypt share to file
			recipient[0] = ent
			if pt, err = openpgp.Encrypt(ct, recipient, nil, nil, nil); err != nil {
				logger.Printf(logger.ERROR, "[sid.upload] Can't create encrypter: %s\n", err.Error())
				ct.Close()
				wrt.Close()
				continue
			}
			pt.Write([]byte(shares[i].P.String() + "\n"))
			pt.Write([]byte(shares[i].X.String() + "\n"))
			pt.Write([]byte(shares[i].Y.String() + "\n"))
			pt.Close()
			ct.Close()
			wrt.Close()
		}
	}
	// report success
	return true
}
Example #18
0
File: socks.go Project: bfix/gospel
// Socks5ConnectTimeout connects to a SOCKS5 proxy with timeout.
func Socks5ConnectTimeout(proto string, addr string, port int, proxy string, timeout time.Duration) (net.Conn, error) {
	var (
		conn net.Conn
		err  error
	)
	if proto != "tcp" {
		logger.Printf(logger.ERROR, "[network] Unsupported protocol '%s'.\n", proto)
		return nil, errors.New("Unsupported protocol (TCP only)")
	}
	p, err := url.Parse(proxy)
	if err != nil {
		return nil, err
	}
	if len(p.Scheme) > 0 && p.Scheme != "socks5" {
		logger.Printf(logger.ERROR, "[network] Invalid proxy scheme '%s'.\n", p.Scheme)
		return nil, errors.New("Invalid proxy scheme")
	}
	idx := strings.Index(p.Host, ":")
	if idx == -1 {
		logger.Printf(logger.ERROR, "[network] Invalid host definition '%s'.\n", p.Host)
		return nil, errors.New("Invalid host definition (missing port)")
	}
	pPort, err := strconv.Atoi(p.Host[idx+1:])
	if err != nil || port < 1 || port > 65535 {
		logger.Printf(logger.ERROR, "[network] Invalid port definition '%d'.\n", pPort)
		return nil, errors.New("Invalid host definition (port out of range)")
	}
	if timeout == 0 {
		conn, err = net.Dial("tcp", p.Host)
	} else {
		conn, err = net.DialTimeout("tcp", p.Host, timeout)
	}
	if err != nil {
		logger.Printf(logger.ERROR, "[network] failed to connect to proxy server: %s\n", err.Error())
		return nil, err
	}

	data := make([]byte, 1024)

	//-----------------------------------------------------------------
	// negotiate authentication
	//-----------------------------------------------------------------
	data[0] = 5 // SOCKS version
	data[1] = 1 // One available authentication method
	data[2] = 0 // No authentication required
	if timeout > 0 {
		conn.SetDeadline(time.Now().Add(timeout))
	}
	if n, err := conn.Write(data[:3]); n != 3 {
		logger.Printf(logger.ERROR, "[network] failed to write to proxy server: %s\n", err.Error())
		conn.Close()
		return nil, err
	}
	if timeout > 0 {
		conn.SetDeadline(time.Now().Add(timeout))
	}
	if n, err := conn.Read(data); n != 2 {
		logger.Printf(logger.ERROR, "[network] failed to read from proxy server: %s\n", err.Error())
		conn.Close()
		return nil, err
	}
	if data[0] != 5 || data[1] == 0xFF {
		logger.Println(logger.ERROR, "[network] proxy server refuses non-authenticated connection.")
		conn.Close()
		return nil, err
	}

	//-----------------------------------------------------------------
	// connect to target (request/reply processing)
	//-----------------------------------------------------------------
	dn := []byte(addr)
	size := len(dn)

	data[0] = 5          // SOCKS versions
	data[1] = 1          // connect to target
	data[2] = 0          // reserved
	data[3] = 3          // domain name specified
	data[4] = byte(size) // length of domain name
	for i, v := range dn {
		data[5+i] = v
	}
	data[5+size] = (byte)(port / 256)
	data[6+size] = (byte)(port % 256)
	if timeout > 0 {
		conn.SetDeadline(time.Now().Add(timeout))
	}
	if n, err := conn.Write(data[:7+size]); n != (7 + size) {
		logger.Printf(logger.ERROR, "[network] failed to write to proxy server: %s\n", err.Error())
		conn.Close()
		return nil, err
	}
	if timeout > 0 {
		conn.SetDeadline(time.Now().Add(timeout))
	}
	_, err = conn.Read(data)
	if err != nil {
		conn.Close()
		return nil, err
	}
	if data[1] != 0 {
		err = errors.New(socksState[data[1]])
		logger.Printf(logger.ERROR, "[network] proxy server failed: %s\n", err.Error())
		conn.Close()
		return nil, err
	}

	//return connection
	return conn, nil
}