Пример #1
0
func (ce *CtrlEngine) upkeepAccounts(
	unmappedID, period, remaining string,
	statfp io.Writer,
) error {
	mappedID, err := identity.Map(unmappedID)
	if err != nil {
		return err
	}

	exec, now, err := checkExecution(mappedID, period,
		func(mappedID string) (int64, error) {
			return ce.msgDB.GetUpkeepAccounts(mappedID)
		})
	if err != nil {
		return err
	}
	if !exec {
		log.Info(statfp, "ctrlengine: upkeep accounts not due")
		fmt.Fprintf(statfp, "ctrlengine: upkeep accounts not due\n")
		return nil
	}

	remain, err := time.ParseDuration(remaining)
	if err != nil {
		return err
	}

	contacts, err := ce.msgDB.GetAccounts(mappedID)
	if err != nil {
		return err
	}

	for _, contact := range contacts {
		privkey, server, _, _, _, _, err := ce.msgDB.GetAccount(mappedID, contact)
		if err != nil {
			return err
		}
		last, err := ce.msgDB.GetAccountTime(mappedID, contact)
		if err != nil {
			return err
		}
		if last == 0 {
			last, err = mixclient.AccountStat(privkey, server, def.CACert)
			if err != nil {
				return err
			}
			err := ce.msgDB.SetAccountTime(mappedID, contact, last)
			if err != nil {
				return err
			}
		}
		if times.Now()+int64(remain.Seconds()) >= last {
			token, err := wallet.GetToken(ce.client, def.AccdUsage, def.AccdOwner)
			if err != nil {
				return err
			}
			_, err = mixclient.PayAccount(privkey, token.Token, server, def.CACert)
			if err != nil {
				ce.client.UnlockToken(token.Hash)
				return log.Error(err)
			}
			ce.client.DelToken(token.Hash)
			last, err = mixclient.AccountStat(privkey, server, def.CACert)
			if err != nil {
				return err
			}
			err = ce.msgDB.SetAccountTime(mappedID, contact, last)
			if err != nil {
				return err
			}
		}
	}

	// record time of execution
	if err := ce.msgDB.SetUpkeepAccounts(mappedID, now); err != nil {
		return err
	}

	return nil
}
Пример #2
0
func mutecryptNewUID(
	c *cli.Context,
	passphrase []byte,
	id, domain, host, mixaddress, nymaddress string,
	client *client.Client,
) error {
	log.Infof("mutecryptNewUID(): id=%s, domain=%s", id, domain)
	args := []string{
		"--homedir", c.GlobalString("homedir"),
		"--loglevel", c.GlobalString("loglevel"),
		"--logdir", c.GlobalString("logdir"),
	}
	if host != "" {
		args = append(args,
			"--keyhost", host,
			"--keyport", ":8080") // TODO: remove keyport hack!
	}
	cmd := exec.Command("mutecrypt", args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	scanner := bufio.NewScanner(stderr)
	passphraseReader, passphraseWriter, err := os.Pipe()
	if err != nil {
		return err
	}
	cmd.ExtraFiles = append(cmd.ExtraFiles, passphraseReader)
	commandReader, commandWriter, err := os.Pipe()
	if err != nil {
		return err
	}
	cmd.ExtraFiles = append(cmd.ExtraFiles, commandReader)

	// generate UID
	_, err = io.WriteString(commandWriter, strings.Join([]string{
		"uid", "generate", "--id", id + "\n",
	}, " "))
	if err != nil {
		return err
	}

	// start process
	if err := cmd.Start(); err != nil {
		return err
	}

	// write passphrase
	plen := len(passphrase)
	buf := make([]byte, plen+1)
	defer bzero.Bytes(buf)
	copy(buf, passphrase)
	copy(buf[plen:], []byte("\n"))
	if _, err := passphraseWriter.Write(buf); err != nil {
		return err
	}
	passphraseWriter.Close()

	// check for errors on stderr
	for scanner.Scan() {
		line := scanner.Text()
		if line != "READY." {
			return errors.New(line)
		}
		break
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	// get capabilities
	args = []string{"caps", "show", "--domain", domain}
	if host := c.String("host"); host != "" {
		args = append(args, "--host", host)
	}
	args = append(args, "\n")
	_, err = io.WriteString(commandWriter, strings.Join(args, " "))
	if err != nil {
		return err
	}
	for scanner.Scan() {
		line := scanner.Text()
		if line != "READY." {
			return errors.New(line)
		}
		break
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	var caps capabilities.Capabilities
	decoder := json.NewDecoder(stdout)
	if err := decoder.Decode(&caps); err != nil {
		return err
	}
	/*
		// pretty-print capabilities
		jsn, err := json.MarshalIndent(caps, "", "  ")
		if err != nil {
			return err
		}
		fmt.Println(string(jsn))
	*/

	owner, err := decodeED25519PubKeyBase64(caps.TKNPUBKEY)
	if err != nil {
		return err
	}
	// get token from wallet
	token, err := wallet.GetToken(client, "UID", owner)
	if err != nil {
		return err
	}

	// try to register UID
	_, err = io.WriteString(commandWriter, strings.Join([]string{
		"uid", "register",
		"--id", id,
		"--token", base64.Encode(token.Token) + "\n",
	}, " "))
	if err != nil {
		client.UnlockToken(token.Hash)
		return err
	}

	var cryptErr error
	for scanner.Scan() {
		line := scanner.Text()
		if line != "READY." {
			cryptErr = errors.New(line)
		} else {
			break
		}
	}
	if err := scanner.Err(); err != nil {
		client.UnlockToken(token.Hash)
		return err
	}

	// delete UID, if registration was not successful
	if cryptErr != nil {
		client.UnlockToken(token.Hash)
		_, err = io.WriteString(commandWriter, strings.Join([]string{
			"uid", "delete",
			"--force",
			"--id", id + "\n",
		}, " "))
		if err != nil {
			return err
		}
		for scanner.Scan() {
			line := scanner.Text()
			if line != "READY." {
				return errors.New(line)
			}
			break
		}
		if err := scanner.Err(); err != nil {
			return err
		}
	} else {
		client.DelToken(token.Hash)
	}

	// add KeyInit messages
	token, err = wallet.GetToken(client, "Message", owner)
	if err != nil {
		return err
	}
	_, err = io.WriteString(commandWriter, strings.Join([]string{
		"keyinit", "add",
		"--id", id,
		"--mixaddress", mixaddress,
		"--nymaddress", nymaddress,
		"--token", base64.Encode(token.Token) + "\n",
	}, " "))
	if err != nil {
		client.UnlockToken(token.Hash)
		return err
	}
	for scanner.Scan() {
		line := scanner.Text()
		if line != "READY." {
			return errors.New(line)
		}
		break
	}
	if err := scanner.Err(); err != nil {
		client.UnlockToken(token.Hash)
		return err
	}
	client.DelToken(token.Hash)

	// quit mutecrypt
	if _, err := io.WriteString(commandWriter, "quit\n"); err != nil {
		return err
	}
	for scanner.Scan() {
		line := scanner.Text()
		if line != "QUITTING" {
			return errors.New(line)
		}
		break
	}
	if err := scanner.Err(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		return err
	}

	// propagate error
	if cryptErr != nil {
		return cryptErr
	}

	return nil
}
Пример #3
0
func (ce *CtrlEngine) procOutQueue(
	c *cli.Context,
	nym string,
	failDelivery bool,
) error {
	log.Debug("procOutQueue()")
	for {
		oqIdx, msg, nymaddress, minDelay, maxDelay, envelope, err :=
			ce.msgDB.GetOutQueue(nym)
		if err != nil {
			return err
		}
		if msg == "" {
			log.Debug("break")
			break // no more messages in outqueue
		}
		if !envelope {
			log.Debug("envelope")
			// parse nymaddress
			na, err := base64.Decode(nymaddress)
			if err != nil {
				return log.Error(na)
			}
			addr, err := nymaddr.ParseAddress(na)
			if err != nil {
				return err
			}
			// get token from wallet
			var pubkey [32]byte
			copy(pubkey[:], addr.TokenPubKey)
			token, err := wallet.GetToken(ce.client, "Message", &pubkey)
			if err != nil {
				return err
			}
			// `muteproto create`
			env, err := muteprotoCreate(c, msg, minDelay, maxDelay,
				base64.Encode(token.Token), nymaddress)
			if err != nil {
				return log.Error(err)
			}
			// update outqueue
			if err := ce.msgDB.SetOutQueue(oqIdx, env); err != nil {
				ce.client.UnlockToken(token.Hash)
				return err
			}
			ce.client.DelToken(token.Hash)
			msg = env
		}
		// `muteproto deliver`
		if failDelivery {
			return log.Error(ErrDeliveryFailed)
		}
		sendTime := times.Now() + int64(minDelay) // earliest
		resend, err := muteprotoDeliver(c, msg)
		if err != nil {
			// If the message delivery failed because the token expired in the
			// meantime we retract the message from the outqueue (setting it
			// back to 'ToSend') and start the delivery process for this
			// message all over again.
			// Matching the error message string is not optimal, but the best
			// available solution since the error results from calling another
			// binary (muteproto).
			if strings.HasSuffix(err.Error(), client.ErrFinal.Error()) {
				log.Debug("retract")
				if err := ce.msgDB.RetractOutQueue(oqIdx); err != nil {
					return err
				}
				continue
			}
			return log.Error(err)
		}
		if resend {
			// set resend status
			log.Debug("resend")
			if err := ce.msgDB.SetResendOutQueue(oqIdx); err != nil {
				return err
			}
		} else {
			// remove from outqueue
			log.Debug("remove")
			if err := ce.msgDB.RemoveOutQueue(oqIdx, sendTime); err != nil {
				return err
			}
		}
	}
	return nil
}
Пример #4
0
func (ce *CtrlEngine) uidNew(
	c *cli.Context,
	minDelay, maxDelay int32,
	host string,
) error {
	// make sure the ID is well-formed
	unmapped := c.String("id")
	id, domain, err := identity.MapPlus(unmapped)
	if err != nil {
		return err
	}

	// sync corresponding hashchain
	if id != "keyserver" {
		if err := ce.upkeepHashchain(c, domain, c.String("host")); err != nil {
			return err
		}
	}

	// check that ID has not been registered already by the same user
	unmappedID, _, err := ce.msgDB.GetNym(id)
	if err != nil {
		return err
	}
	if unmappedID != "" {
		return log.Error(ErrUserIDOwned)
	}

	// check that ID has not been registered already by other user
	err = mutecryptHashchainSearch(c, id, c.String("host"), ce.passphrase)
	if err == nil {
		return log.Error(ErrUserIDTaken)
	}

	// get token from wallet
	token, err := wallet.GetToken(ce.client, def.AccdUsage, def.AccdOwner)
	if err != nil {
		return err
	}

	// register account for  UID
	_, privkey, err := ed25519.GenerateKey(cipher.RandReader)
	if err != nil {
		return log.Error(err)
	}
	server, err := mixclient.PayAccount(privkey, token.Token, "", def.CACert)
	if err != nil {
		ce.client.UnlockToken(token.Hash)
		return log.Error(err)
	}
	ce.client.DelToken(token.Hash)

	// generate secret for account
	var secret [64]byte
	if _, err := io.ReadFull(cipher.RandReader, secret[:]); err != nil {
		return err
	}

	// get mixaddress and nymaddress for KeyInit message
	expire := times.ThirtyDaysLater() // TODO: make this settable
	singleUse := false                // TODO correct?
	var pubkey [ed25519.PublicKeySize]byte
	copy(pubkey[:], privkey[32:])
	mixaddress, nymaddress, err := util.NewNymAddress(domain, secret[:], expire,
		singleUse, minDelay, maxDelay, id, &pubkey, server, def.CACert)
	if err != nil {
		return err
	}

	// generate UID
	err = mutecryptNewUID(c, ce.passphrase, id, domain, host, mixaddress,
		nymaddress, ce.client)
	if err != nil {
		return err
	}

	// save name mapping
	if err := ce.msgDB.AddNym(id, unmapped, c.String("full-name")); err != nil {
		return err
	}

	// register account for UID
	err = ce.msgDB.AddAccount(id, "", privkey, server, &secret,
		minDelay, maxDelay)
	if err != nil {
		return err
	}

	// set active UID, if this was the first UID
	active, err := ce.msgDB.GetValue(msgdb.ActiveUID)
	if err != nil {
		return err
	}
	if active == "" {
		if err := ce.msgDB.AddValue(msgdb.ActiveUID, unmapped); err != nil {
			return err
		}
	}
	return nil
}