check_SET_VLAN_VID(struct ofpact_vlan_vid *a, struct ofpact_check_params *cp)
{
    /* Remember if we saw a vlan tag in the flow to aid translating to OpenFlow
     * 1.1+ if need be. */
    ovs_be16 *tci = &cp->match->flow.vlans[0].tci;
    a->flow_has_vlan = (*tci & htons(VLAN_CFI)) != 0;
    if (!a->flow_has_vlan && !a->push_vlan_if_needed) {
        inconsistent_match(&cp->usable_protocols);
    }

    /* Temporarily mark that we have a vlan tag. */
    *tci |= htons(VLAN_CFI);

    return 0;
}


ofp-actions: Fix use-after-free while decoding RAW_ENCAP.

While decoding RAW_ENCAP action, decode_ed_prop() might re-allocate
ofpbuf if there is no enough space left.  However, function
'decode_NXAST_RAW_ENCAP' continues to use old pointer to 'encap'
structure leading to write-after-free and incorrect decoding.

  ==3549105==ERROR: AddressSanitizer: heap-use-after-free on address
  0x60600000011a at pc 0x0000005f6cc6 bp 0x7ffc3a2d4410 sp 0x7ffc3a2d4408
  WRITE of size 2 at 0x60600000011a thread T0
    #0 0x5f6cc5 in decode_NXAST_RAW_ENCAP lib/ofp-actions.c:4461:20
    #1 0x5f0551 in ofpact_decode ./lib/ofp-actions.inc2:4777:16
    #2 0x5ed17c in ofpacts_decode lib/ofp-actions.c:7752:21
    #3 0x5eba9a in ofpacts_pull_openflow_actions__ lib/ofp-actions.c:7791:13
    #4 0x5eb9fc in ofpacts_pull_openflow_actions lib/ofp-actions.c:7835:12
    #5 0x64bb8b in ofputil_decode_packet_out lib/ofp-packet.c:1113:17
    #6 0x65b6f4 in ofp_print_packet_out lib/ofp-print.c:148:13
    #7 0x659e3f in ofp_to_string__ lib/ofp-print.c:1029:16
    #8 0x659b24 in ofp_to_string lib/ofp-print.c:1244:21
    #9 0x65a28c in ofp_print lib/ofp-print.c:1288:28
    #10 0x540d11 in ofctl_ofp_parse utilities/ovs-ofctl.c:2814:9
    #11 0x564228 in ovs_cmdl_run_command__ lib/command-line.c:247:17
    #12 0x56408a in ovs_cmdl_run_command lib/command-line.c:278:5
    #13 0x5391ae in main utilities/ovs-ofctl.c:179:9
    #14 0x7f6911ce9081 in __libc_start_main (/lib64/libc.so.6+0x27081)
    #15 0x461fed in _start (utilities/ovs-ofctl+0x461fed)

Fix that by getting a new pointer before using.

Credit to OSS-Fuzz.

Fuzzer regression test will fail only with AddressSanitizer enabled.

Reported-at: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27851
Fixes: f839892a206a ("OF support and translation of generic encap and decap")
Acked-by: William Tu <u9012063@gmail.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>

parse_reg_load(char *arg, const struct ofpact_parse_params *pp)
{
    struct mf_subfield dst;
    char *key, *value_str;
    union mf_value value;
    char *error;

    error = set_field_split_str(arg, &key, &value_str, NULL);
    if (error) {
        return error;
    }

    error = mf_parse_subfield(&dst, key);
    if (error) {
        return error;
    }

    if (parse_int_string(value_str, (uint8_t *)&value, dst.field->n_bytes,
                         &key)) {
        return xasprintf("%s: cannot parse integer value", arg);
    }

    if (!bitwise_is_all_zeros(&value, dst.field->n_bytes, dst.n_bits,
                              dst.field->n_bytes * 8 - dst.n_bits)) {
        struct ds ds;

        ds_init(&ds);
        mf_format(dst.field, &value, NULL, NULL, &ds);
        error = xasprintf("%s: value %s does not fit into %d bits",
                          arg, ds_cstr(&ds), dst.n_bits);
        ds_destroy(&ds);
        return error;
    }

    struct ofpact_set_field *sf = ofpact_put_reg_load(pp->ofpacts, dst.field,
                                                      NULL, NULL);

    bitwise_copy(&value, dst.field->n_bytes, 0, sf->value,
                 dst.field->n_bytes, dst.ofs, dst.n_bits);
    bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes, dst.ofs,
                dst.n_bits);
    return NULL;
}

Open vSwitch (aka openvswitch) 2.11.0 through 2.15.0 has a use-after-free in decode_NXAST_RAW_ENCAP (called from ofpact_decode and ofpacts_decode) during the decoding of a RAW_ENCAP action.


parse_STACK_PUSH(char *arg, const struct ofpact_parse_params *pp)
{
    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(pp->ofpacts), arg);
}


parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_CLEAR_ACTIONS(pp->ofpacts);
    return NULL;
}


ofpact_format(const struct ofpact *a,
              const struct ofpact_format_params *fp)
{
    switch (a->type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
        case OFPACT_##ENUM:                                             \
            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), fp);  \
            break;
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}


parse_SET_ETH_DST(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(pp->ofpacts)->mac);
}


ofpacts_parse_copy(const char *s_, const struct ofpact_parse_params *pp,
                   bool allow_instructions, enum ofpact_type outer_action)
{
    char *error, *s;

    *pp->usable_protocols = OFPUTIL_P_ANY;

    s = xstrdup(s_);
    error = ofpacts_parse(s, pp, allow_instructions, outer_action);
    free(s);

    return error;
}


check_CONJUNCTION(const struct ofpact_conjunction *a OVS_UNUSED,
                  const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


check_MULTIPATH(const struct ofpact_multipath *a,
                const struct ofpact_check_params *cp)
{
    return multipath_check(a, cp->match);
}


parse_ENCAP(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_encap *encap;
    char *key, *value, *str;
    char *error = NULL;
    uint16_t prop_class;
    int n_props = 0;

    encap = ofpact_put_ENCAP(pp->ofpacts);
    encap->hdr_size = 0;
    /* Parse encap header type. */
    str = arg;
    if (!ofputil_parse_key_value(&arg, &key, &value)) {
        return xasprintf("Missing encap hdr: %s", str);
    }
    if (!parse_encap_header(key, &encap->new_pkt_type)) {
        return xasprintf("Encap hdr not supported: %s", value);
    }
    if (!parse_ed_prop_class(key, &prop_class)) {
        return xasprintf("Invalid encap prop class: %s", key);
    }
    /* Parse encap properties. */
    error = parse_ed_props(prop_class, &value, &n_props, pp->ofpacts);
    if (error != NULL) {
        return error;
    }
    /* ofpbuf may have been re-allocated. */
    encap = pp->ofpacts->header;
    encap->n_props = n_props;
    ofpact_finish_ENCAP(pp->ofpacts, &encap);
    return NULL;
}


parse_GROUP(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_u32(arg, &ofpact_put_GROUP(pp->ofpacts)->group_id);
}


format_CONJUNCTION(const struct ofpact_conjunction *oc,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
                  colors.paren, colors.end,
                  oc->id, oc->clause + 1, oc->n_clauses,
                  colors.paren, colors.end);
}


decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
                    enum ofp_version ofp_version,
                    const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap,
                    struct ofpbuf *out)
{
    const size_t ct_offset = ofpacts_pull(out);
    struct ofpact_conntrack *conntrack = ofpact_put_CT(out);
    int error;

    conntrack->flags = ntohs(nac->flags);
    if (conntrack->flags & NX_CT_F_FORCE &&
        !(conntrack->flags & NX_CT_F_COMMIT)) {
        error = OFPERR_OFPBAC_BAD_ARGUMENT;
        goto out;
    }

    error = decode_ct_zone(nac, conntrack, vl_mff_map, tlv_bitmap);
    if (error) {
        goto out;
    }
    conntrack->recirc_table = nac->recirc_table;
    conntrack->alg = ntohs(nac->alg);

    ofpbuf_pull(out, sizeof(*conntrack));

    struct ofpbuf openflow = ofpbuf_const_initializer(
        nac + 1, ntohs(nac->len) - sizeof(*nac));
    error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
                                            ofp_version,
                                            1u << OVSINST_OFPIT11_APPLY_ACTIONS,
                                            out, OFPACT_CT, vl_mff_map,
                                            tlv_bitmap);
    if (error) {
        return error;
    }

    conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack));
    out->header = &conntrack->ofpact;
    ofpact_finish_CT(out, &conntrack);

    if (conntrack->ofpact.len > sizeof(*conntrack)
        && !(conntrack->flags & NX_CT_F_COMMIT)) {
        const struct ofpact *a;
        size_t ofpacts_len = conntrack->ofpact.len - sizeof(*conntrack);

        OFPACT_FOR_EACH (a, conntrack->actions, ofpacts_len) {
            if (a->type != OFPACT_NAT || ofpact_get_NAT(a)->flags
                || ofpact_get_NAT(a)->range_af != AF_UNSPEC) {
                VLOG_WARN_RL(&rl, "CT action requires commit flag if actions "
                             "other than NAT without arguments are specified.");
                error = OFPERR_OFPBAC_BAD_ARGUMENT;
                goto out;
            }
        }
    }

out:
    ofpbuf_push_uninit(out, ct_offset);
    return error;
}


format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED,
                const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sct_clear%s", colors.value, colors.end);
}


ofpacts_equal_stringwise(const struct ofpact *a, size_t a_len,
                         const struct ofpact *b, size_t b_len)
{
    struct ds a_s = DS_EMPTY_INITIALIZER;
    struct ofpact_format_params a_fp = { .s = &a_s };
    ofpacts_format(a, a_len, &a_fp);

    struct ds b_s = DS_EMPTY_INITIALIZER;
    struct ofpact_format_params b_fp = { .s = &b_s };
    ofpacts_format(b, b_len, &b_fp);

    bool equal = !strcmp(ds_cstr(&a_s), ds_cstr(&b_s));

    ds_destroy(&a_s);
    ds_destroy(&b_s);

    return equal;
}


decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
                       enum ofp_version ofp_version OVS_UNUSED,
                       const struct vl_mff_map *vl_mff_map,
                       uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpact_learn *learn;
    enum ofperr error;

    learn = ofpact_put_LEARN(ofpacts);

    error = decode_LEARN_common(nal, NXAST_RAW_LEARN, learn);
    if (error) {
        return error;
    }

    if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM |
                         NX_LEARN_F_DELETE_LEARNED)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return decode_LEARN_specs(nal + 1, (char *) nal + ntohs(nal->len),
                              vl_mff_map, tlv_bitmap, ofpacts);
}


decode_NXAST_RAW_DEC_NSH_TTL(struct ofpbuf *out)
{
    ofpact_put_DEC_NSH_TTL(out);
    return 0;
}


parse_set_vlan_pcp(char *arg, bool push_vlan_if_needed,
                   const struct ofpact_parse_params *pp)
{
    struct ofpact_vlan_pcp *vlan_pcp;
    uint8_t pcp;
    char *error;

    error = str_to_u8(arg, "VLAN PCP", &pcp);
    if (error) {
        return error;
    }

    if (pcp & ~7) {
        return xasprintf("%s: not a valid VLAN PCP", arg);
    }
    vlan_pcp = ofpact_put_SET_VLAN_PCP(pp->ofpacts);
    vlan_pcp->vlan_pcp = pcp;
    vlan_pcp->push_vlan_if_needed = push_vlan_if_needed;
    return NULL;
}


format_encap_pkt_type(const ovs_be32 pkt_type)
{
    switch (ntohl(pkt_type)) {
    case PT_ETH:
        return "ethernet";
    case PT_NSH:
        return "nsh";
    default:
        return "UNKNOWN";
    }
}


ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
               const struct ofpact_format_params *fp)
{
    if (!ofpacts_len) {
        ds_put_format(fp->s, "%sdrop%s", colors.drop, colors.end);
    } else {
        const struct ofpact *a;

        OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
            if (a != ofpacts) {
                ds_put_char(fp->s, ',');
            }

            ofpact_format(a, fp);
        }
    }
}


encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED,
                    enum ofp_version ofp_version OVS_UNUSED,
                    struct ofpbuf *out)
{
    put_NXAST_DEBUG_RECIRC(out);
}


format_LEARN(const struct ofpact_learn *a,
             const struct ofpact_format_params *fp)
{
    learn_format(a, fp->port_map, fp->table_map, fp->s);
}


parse_EXIT(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_EXIT(pp->ofpacts);
    return NULL;
}


format_SET_ETH_SRC(const struct ofpact_mac *a,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_dl_src:%s"ETH_ADDR_FMT,
                  colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
}


check_set_mpls(struct ofpact_check_params *cp)
{
    ovs_be16 dl_type = get_dl_type(&cp->match->flow);
    if (!eth_type_mpls(dl_type)) {
        inconsistent_match(&cp->usable_protocols);
    }
    return 0;
}


parse_CT(char *arg, const struct ofpact_parse_params *pp)
{
    const size_t ct_offset = ofpacts_pull(pp->ofpacts);
    struct ofpact_conntrack *oc;
    char *error = NULL;
    char *key, *value;

    oc = ofpact_put_CT(pp->ofpacts);
    oc->flags = 0;
    oc->recirc_table = NX_CT_RECIRC_NONE;
    while (ofputil_parse_key_value(&arg, &key, &value)) {
        if (!strcmp(key, "commit")) {
            oc->flags |= NX_CT_F_COMMIT;
        } else if (!strcmp(key, "force")) {
            oc->flags |= NX_CT_F_FORCE;
        } else if (!strcmp(key, "table")) {
            if (!ofputil_table_from_string(value, pp->table_map,
                                           &oc->recirc_table)) {
                error = xasprintf("unknown table %s", value);
            } else if (oc->recirc_table == NX_CT_RECIRC_NONE) {
                error = xasprintf("invalid table %#"PRIx8, oc->recirc_table);
            }
        } else if (!strcmp(key, "zone")) {
            error = str_to_u16(value, "zone", &oc->zone_imm);

            if (error) {
                free(error);
                error = mf_parse_subfield(&oc->zone_src, value);
                if (error) {
                    return error;
                }
            }
        } else if (!strcmp(key, "alg")) {
            error = str_to_connhelper(value, &oc->alg);
        } else if (!strcmp(key, "nat")) {
            const size_t nat_offset = ofpacts_pull(pp->ofpacts);

            error = parse_NAT(value, pp);
            /* Update CT action pointer and length. */
            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, nat_offset);
            oc = pp->ofpacts->header;
        } else if (!strcmp(key, "exec")) {
            /* Hide existing actions from ofpacts_parse_copy(), so the
             * nesting can be handled transparently. */
            enum ofputil_protocol usable_protocols2;
            const size_t exec_offset = ofpacts_pull(pp->ofpacts);

            /* Initializes 'usable_protocol2', fold it back to
             * '*usable_protocols' afterwards, so that we do not lose
             * restrictions already in there. */
            struct ofpact_parse_params pp2 = *pp;
            pp2.usable_protocols = &usable_protocols2;
            error = ofpacts_parse_copy(value, &pp2, false, OFPACT_CT);
            *pp->usable_protocols &= usable_protocols2;
            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, exec_offset);
            oc = pp->ofpacts->header;
        } else {
            error = xasprintf("invalid argument to \"ct\" action: `%s'", key);
        }
        if (error) {
            break;
        }
    }
    if (!error && oc->flags & NX_CT_F_FORCE && !(oc->flags & NX_CT_F_COMMIT)) {
        error = xasprintf("\"force\" flag requires \"commit\" flag.");
    }
    ofpact_finish_CT(pp->ofpacts, &oc);
    ofpbuf_push_uninit(pp->ofpacts, ct_offset);
    return error;
}


ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
                                   unsigned int instructions_len,
                                   enum ofp_version version,
                                   const struct vl_mff_map *vl_mff_map,
                                   uint64_t *ofpacts_tlv_bitmap,
                                   struct ofpbuf *ofpacts)
{
    const struct ofp11_instruction *instructions;
    const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
    enum ofperr error;

    ofpbuf_clear(ofpacts);
    if (version == OFP10_VERSION) {
        return ofpacts_pull_openflow_actions__(openflow, instructions_len,
                                               version,
                                               (1u << N_OVS_INSTRUCTIONS) - 1,
                                               ofpacts, 0, vl_mff_map,
                                               ofpacts_tlv_bitmap);
    }

    if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
        VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a "
                     "multiple of %d",
                     instructions_len, OFP11_INSTRUCTION_ALIGN);
        error = OFPERR_OFPBIC_BAD_LEN;
        goto exit;
    }

    instructions = ofpbuf_try_pull(openflow, instructions_len);
    if (instructions == NULL) {
        VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds "
                     "remaining message length (%"PRIu32")",
                     instructions_len, openflow->size);
        error = OFPERR_OFPBIC_BAD_LEN;
        goto exit;
    }

    error = decode_openflow11_instructions(
        instructions, instructions_len / OFP11_INSTRUCTION_ALIGN,
        insts);
    if (error) {
        goto exit;
    }

    if (insts[OVSINST_OFPIT13_METER]) {
        const struct ofp13_instruction_meter *oim;
        struct ofpact_meter *om;

        oim = ALIGNED_CAST(const struct ofp13_instruction_meter *,
                           insts[OVSINST_OFPIT13_METER]);

        om = ofpact_put_METER(ofpacts);
        om->meter_id = ntohl(oim->meter_id);
        om->provider_meter_id = UINT32_MAX; /* No provider meter ID. */
    }
    if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
        const struct ofp_action_header *actions;
        size_t actions_len;

        get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
                                     &actions, &actions_len);
        error = ofpacts_decode(actions, actions_len, version, vl_mff_map,
                               ofpacts_tlv_bitmap, ofpacts);
        if (error) {
            goto exit;
        }
    }
    if (insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) {
        instruction_get_OFPIT11_CLEAR_ACTIONS(
            insts[OVSINST_OFPIT11_CLEAR_ACTIONS]);
        ofpact_put_CLEAR_ACTIONS(ofpacts);
    }
    if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
        struct ofpact_nest *on;
        const struct ofp_action_header *actions;
        size_t actions_len;
        size_t start = ofpacts->size;
        ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS,
                   offsetof(struct ofpact_nest, actions));
        get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS],
                                     &actions, &actions_len);
        error = ofpacts_decode_for_action_set(actions, actions_len,
                                              version, vl_mff_map,
                                              ofpacts_tlv_bitmap, ofpacts);
        if (error) {
            goto exit;
        }
        on = ofpbuf_at_assert(ofpacts, start, sizeof *on);
        on->ofpact.len = ofpacts->size - start;
    }
    if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
        const struct ofp11_instruction_write_metadata *oiwm;
        struct ofpact_metadata *om;

        oiwm = ALIGNED_CAST(const struct ofp11_instruction_write_metadata *,
                            insts[OVSINST_OFPIT11_WRITE_METADATA]);

        om = ofpact_put_WRITE_METADATA(ofpacts);
        om->metadata = oiwm->metadata;
        om->mask = oiwm->metadata_mask;
    }
    if (insts[OVSINST_OFPIT11_GOTO_TABLE]) {
        const struct ofp11_instruction_goto_table *oigt;
        struct ofpact_goto_table *ogt;

        oigt = instruction_get_OFPIT11_GOTO_TABLE(
            insts[OVSINST_OFPIT11_GOTO_TABLE]);
        ogt = ofpact_put_GOTO_TABLE(ofpacts);
        ogt->table_id = oigt->table_id;
    }

    error = ofpacts_verify(ofpacts->data, ofpacts->size,
                           (1u << N_OVS_INSTRUCTIONS) - 1, 0);
exit:
    if (error) {
        ofpbuf_clear(ofpacts);
    }
    return error;
}


format_POP_MPLS(const struct ofpact_pop_mpls *a,
                const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%spop_mpls:%s0x%04"PRIx16,
                  colors.param, colors.end, ntohs(a->ethertype));
}


ofpacts_parse_actions(const char *s, const struct ofpact_parse_params *pp)
{
    return ofpacts_parse_copy(s, pp, false, 0);
}


parse_UNROLL_XLATE(char *arg OVS_UNUSED,
                   const struct ofpact_parse_params *pp OVS_UNUSED)
{
    return xasprintf("UNROLL is an internal action "
                     "that shouldn't be used via OpenFlow");
}


format_SET_MPLS_TC(const struct ofpact_mpls_tc *a,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                  colors.paren, colors.end, a->tc,
                  colors.paren, colors.end);
}


format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                  colors.paren, colors.end, a->ttl,
                  colors.paren, colors.end);
}


parse_SET_L4_SRC_PORT(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_u16(arg, "source port",
                      &ofpact_put_SET_L4_SRC_PORT(pp->ofpacts)->port);
}


check_REG_MOVE(const struct ofpact_reg_move *a,
               const struct ofpact_check_params *cp)
{
    return nxm_reg_move_check(a, cp->match);
}


format_DEC_NSH_TTL(const struct ofpact_null *a OVS_UNUSED,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sdec_nsh_ttl%s", colors.special, colors.end);
}


parse_SAMPLE(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_sample *os = ofpact_put_SAMPLE(pp->ofpacts);
    os->sampling_port = OFPP_NONE;
    os->direction = NX_ACTION_SAMPLE_DEFAULT;

    char *key, *value;
    while (ofputil_parse_key_value(&arg, &key, &value)) {
        char *error = NULL;

        if (!strcmp(key, "probability")) {
            error = str_to_u16(value, "probability", &os->probability);
            if (!error && os->probability == 0) {
                error = xasprintf("invalid probability value \"%s\"", value);
            }
        } else if (!strcmp(key, "collector_set_id")) {
            error = str_to_u32(value, &os->collector_set_id);
        } else if (!strcmp(key, "obs_domain_id")) {
            error = str_to_u32(value, &os->obs_domain_id);
        } else if (!strcmp(key, "obs_point_id")) {
            error = str_to_u32(value, &os->obs_point_id);
        } else if (!strcmp(key, "sampling_port")) {
            if (!ofputil_port_from_string(value, pp->port_map,
                                          &os->sampling_port)) {
                error = xasprintf("%s: unknown port", value);
            }
        } else if (!strcmp(key, "ingress")) {
            os->direction = NX_ACTION_SAMPLE_INGRESS;
        } else if (!strcmp(key, "egress")) {
            os->direction = NX_ACTION_SAMPLE_EGRESS;
        } else {
            error = xasprintf("invalid key \"%s\" in \"sample\" argument",
                              key);
        }
        if (error) {
            return error;
        }
    }
    if (os->probability == 0) {
        return xstrdup("non-zero \"probability\" must be specified on sample");
    }

    return NULL;
}


ofpact_next_flattened(const struct ofpact *ofpact)
{
    switch (ofpact->type) {
    case OFPACT_OUTPUT:
    case OFPACT_GROUP:
    case OFPACT_CONTROLLER:
    case OFPACT_ENQUEUE:
    case OFPACT_OUTPUT_REG:
    case OFPACT_OUTPUT_TRUNC:
    case OFPACT_BUNDLE:
    case OFPACT_SET_FIELD:
    case OFPACT_SET_VLAN_VID:
    case OFPACT_SET_VLAN_PCP:
    case OFPACT_STRIP_VLAN:
    case OFPACT_PUSH_VLAN:
    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_ETH_DST:
    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_IPV4_DST:
    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_L4_DST_PORT:
    case OFPACT_REG_MOVE:
    case OFPACT_STACK_PUSH:
    case OFPACT_STACK_POP:
    case OFPACT_DEC_TTL:
    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_DEC_MPLS_TTL:
    case OFPACT_PUSH_MPLS:
    case OFPACT_POP_MPLS:
    case OFPACT_SET_TUNNEL:
    case OFPACT_SET_QUEUE:
    case OFPACT_POP_QUEUE:
    case OFPACT_FIN_TIMEOUT:
    case OFPACT_RESUBMIT:
    case OFPACT_LEARN:
    case OFPACT_CONJUNCTION:
    case OFPACT_MULTIPATH:
    case OFPACT_NOTE:
    case OFPACT_EXIT:
    case OFPACT_SAMPLE:
    case OFPACT_UNROLL_XLATE:
    case OFPACT_CT_CLEAR:
    case OFPACT_DEBUG_RECIRC:
    case OFPACT_DEBUG_SLOW:
    case OFPACT_METER:
    case OFPACT_CLEAR_ACTIONS:
    case OFPACT_WRITE_METADATA:
    case OFPACT_GOTO_TABLE:
    case OFPACT_NAT:
    case OFPACT_ENCAP:
    case OFPACT_DECAP:
    case OFPACT_DEC_NSH_TTL:
        return ofpact_next(ofpact);

    case OFPACT_CLONE:
        return ofpact_get_CLONE(ofpact)->actions;

    case OFPACT_CT:
        return ofpact_get_CT(ofpact)->actions;

    case OFPACT_WRITE_ACTIONS:
        return ofpact_get_WRITE_ACTIONS(ofpact)->actions;
    }

    OVS_NOT_REACHED();
}


parse_CT_CLEAR(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_CT_CLEAR(pp->ofpacts);
    return NULL;
}


check_PUSH_VLAN(const struct ofpact_push_vlan *a OVS_UNUSED,
                struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;
    if (flow->vlans[FLOW_MAX_VLAN_HEADERS - 1].tci & htons(VLAN_CFI)) {
        /* Support maximum (FLOW_MAX_VLAN_HEADERS) VLAN headers. */
        return OFPERR_OFPBAC_BAD_TAG;
    }
    /* Temporary mark that we have a vlan tag. */
    flow_push_vlan_uninit(flow, NULL);
    flow->vlans[0].tci |= htons(VLAN_CFI);
    return 0;
}


ofpact_decode_raw(enum ofp_version ofp_version,
                  const struct ofp_action_header *oah, size_t length,
                  const struct ofpact_raw_instance **instp)
{
    const struct ofpact_raw_instance *inst;
    struct ofpact_hdrs hdrs;

    *instp = NULL;
    if (length < sizeof *oah) {
        return OFPERR_OFPBAC_BAD_LEN;
    }

    /* Get base action type. */
    if (oah->type == htons(OFPAT_VENDOR)) {
        /* Get vendor. */
        hdrs.vendor = ntohl(oah->vendor);
        if (hdrs.vendor == NX_VENDOR_ID || hdrs.vendor == ONF_VENDOR_ID) {
            /* Get extension subtype. */
            const struct ext_action_header *nah;

            nah = ALIGNED_CAST(const struct ext_action_header *, oah);
            if (length < sizeof *nah) {
                return OFPERR_OFPBAC_BAD_LEN;
            }
            hdrs.type = ntohs(nah->subtype);
        } else {
            VLOG_WARN_RL(&rl, "OpenFlow action has unknown vendor %#"PRIx32,
                         hdrs.vendor);
            return OFPERR_OFPBAC_BAD_VENDOR;
        }
    } else {
        hdrs.vendor = 0;
        hdrs.type = ntohs(oah->type);
    }

    hdrs.ofp_version = ofp_version;
    HMAP_FOR_EACH_WITH_HASH (inst, decode_node, ofpact_hdrs_hash(&hdrs),
                             ofpact_decode_hmap()) {
        if (ofpact_hdrs_equal(&hdrs, &inst->hdrs)) {
            *instp = inst;
            return 0;
        }
    }

    VLOG_WARN_RL(&rl, "unknown %s action for vendor %#"PRIx32" and "
                 "type %"PRIu16, ofputil_version_to_string(ofp_version),
                 hdrs.vendor, hdrs.type);
    return (hdrs.vendor
            ? OFPERR_OFPBAC_BAD_VENDOR_TYPE
            : OFPERR_OFPBAC_BAD_TYPE);
}


parse_pop_vlan(const struct ofpact_parse_params *pp)
{
    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN;
    return NULL;
}


check_NAT(const struct ofpact_nat *a, const struct ofpact_check_params *cp)
{
    ovs_be16 dl_type = get_dl_type(&cp->match->flow);
    if (!dl_type_is_ip_any(dl_type) ||
        (a->range_af == AF_INET && dl_type != htons(ETH_TYPE_IP)) ||
        (a->range_af == AF_INET6 && dl_type != htons(ETH_TYPE_IPV6))) {
        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
    }
    return 0;
}


check_SET_QUEUE(const struct ofpact_queue *a OVS_UNUSED,
                const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


decode_NXAST_RAW_CONTROLLER2(const struct ext_action_header *eah,
                             enum ofp_version ofp_version OVS_UNUSED,
                             struct ofpbuf *out)
{
    if (!is_all_zeros(eah->pad, sizeof eah->pad)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    size_t start_ofs = out->size;
    struct ofpact_controller *oc = ofpact_put_CONTROLLER(out);
    oc->ofpact.raw = NXAST_RAW_CONTROLLER2;
    oc->max_len = UINT16_MAX;
    oc->reason = OFPR_ACTION;
    oc->meter_id = NX_CTLR_NO_METER;

    struct ofpbuf properties;
    ofpbuf_use_const(&properties, eah, ntohs(eah->len));
    ofpbuf_pull(&properties, sizeof *eah);

    while (properties.size > 0) {
        struct ofpbuf payload;
        uint64_t type;

        enum ofperr error = ofpprop_pull(&properties, &payload, &type);
        if (error) {
            return error;
        }

        switch (type) {
        case NXAC2PT_MAX_LEN:
            error = ofpprop_parse_u16(&payload, &oc->max_len);
            break;

        case NXAC2PT_CONTROLLER_ID:
            error = ofpprop_parse_u16(&payload, &oc->controller_id);
            break;

        case NXAC2PT_REASON: {
            uint8_t u8;
            error = ofpprop_parse_u8(&payload, &u8);
            oc->reason = u8;
            break;
        }

        case NXAC2PT_USERDATA:
            out->size = start_ofs + sizeof(struct ofpact_controller);
            ofpbuf_put(out, payload.msg, ofpbuf_msgsize(&payload));
            oc = ofpbuf_at_assert(out, start_ofs, sizeof *oc);
            oc->userdata_len = ofpbuf_msgsize(&payload);
            break;

        case NXAC2PT_PAUSE:
            oc->pause = true;
            break;

        case NXAC2PT_METER_ID:
            error = ofpprop_parse_u32(&payload, &oc->meter_id);
            break;

        default:
            error = OFPPROP_UNKNOWN(false, "NXAST_RAW_CONTROLLER2", type);
            break;
        }
        if (error) {
            return error;
        }
    }

    ofpact_finish_CONTROLLER(out, &oc);

    return 0;
}


parse_set_tunnel(char *arg, enum ofp_raw_action_type raw,
                 const struct ofpact_parse_params *pp)
{
    struct ofpact_tunnel *tunnel;

    tunnel = ofpact_put_SET_TUNNEL(pp->ofpacts);
    tunnel->ofpact.raw = raw;
    return str_to_u64(arg, &tunnel->tun_id);
}


check_DEBUG_SLOW(const struct ofpact_null *a OVS_UNUSED,
                 const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


check_FIN_TIMEOUT(const struct ofpact_fin_timeout *a OVS_UNUSED,
                  struct ofpact_check_params *cp)
{
    if (cp->match->flow.nw_proto != IPPROTO_TCP) {
        inconsistent_match(&cp->usable_protocols);
    }
    return 0;
}


format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sdebug_recirc%s", colors.value, colors.end);
}


parse_encap_header(const char *hdr, ovs_be32 *packet_type)
{
    if (strcmp(hdr, "ethernet") == 0) {
        *packet_type = htonl(PT_ETH);
    } else if (strcmp(hdr, "nsh") == 0) {
        *packet_type = htonl(PT_NSH);
    } else {
        return false;
    }
    return true;
}


parse_noargs_dec_ttl(const struct ofpact_parse_params *pp)
{
    struct ofpact_cnt_ids *ids;
    uint16_t id = 0;

    ofpact_put_DEC_TTL(pp->ofpacts);
    ofpbuf_put(pp->ofpacts, &id, sizeof id);
    ids = pp->ofpacts->header;
    ids->n_controllers++;
    ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
}


decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac,
                            enum ofp_version ofp_version OVS_UNUSED,
                            struct ofpbuf *out)
{
    struct ofpact_controller *oc;

    oc = ofpact_put_CONTROLLER(out);
    oc->ofpact.raw = NXAST_RAW_CONTROLLER;
    oc->max_len = ntohs(nac->max_len);
    oc->controller_id = ntohs(nac->controller_id);
    oc->reason = nac->reason;
    oc->meter_id = NX_CTLR_NO_METER;
    ofpact_finish_CONTROLLER(out, &oc);

    return 0;
}


check_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED,
                    const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


parse_STRIP_VLAN(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
    return NULL;
}


format_NOTE(const struct ofpact_note *a,
            const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%snote:%s", colors.param, colors.end);
    format_hex_arg(fp->s, a->data, a->length);
}


format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sdec_mpls_ttl%s", colors.value, colors.end);
}


format_RESUBMIT(const struct ofpact_resubmit *a,
                const struct ofpact_format_params *fp)
{
    if (a->in_port != OFPP_IN_PORT && a->table_id == 255) {
        ds_put_format(fp->s, "%sresubmit:%s", colors.special, colors.end);
        ofputil_format_port(a->in_port, fp->port_map, fp->s);
    } else {
        ds_put_format(fp->s, "%sresubmit(%s", colors.paren, colors.end);
        if (a->in_port != OFPP_IN_PORT) {
            ofputil_format_port(a->in_port, fp->port_map, fp->s);
        }
        ds_put_char(fp->s, ',');
        if (a->table_id != 255) {
            ofputil_format_table(a->table_id, fp->table_map, fp->s);
        }
        if (a->with_ct_orig) {
            ds_put_cstr(fp->s, ",ct");
        }
        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
    }
}


get_ofpact_map(enum ofp_version version)
{
    /* OpenFlow 1.0 actions. */
    static const struct ofpact_map of10[] = {
        { OFPACT_OUTPUT, 0 },
        { OFPACT_SET_VLAN_VID, 1 },
        { OFPACT_SET_VLAN_PCP, 2 },
        { OFPACT_STRIP_VLAN, 3 },
        { OFPACT_SET_ETH_SRC, 4 },
        { OFPACT_SET_ETH_DST, 5 },
        { OFPACT_SET_IPV4_SRC, 6 },
        { OFPACT_SET_IPV4_DST, 7 },
        { OFPACT_SET_IP_DSCP, 8 },
        { OFPACT_SET_L4_SRC_PORT, 9 },
        { OFPACT_SET_L4_DST_PORT, 10 },
        { OFPACT_ENQUEUE, 11 },
        { 0, -1 },
    };

    /* OpenFlow 1.1 actions. */
    static const struct ofpact_map of11[] = {
        { OFPACT_OUTPUT, 0 },
        { OFPACT_SET_VLAN_VID, 1 },
        { OFPACT_SET_VLAN_PCP, 2 },
        { OFPACT_SET_ETH_SRC, 3 },
        { OFPACT_SET_ETH_DST, 4 },
        { OFPACT_SET_IPV4_SRC, 5 },
        { OFPACT_SET_IPV4_DST, 6 },
        { OFPACT_SET_IP_DSCP, 7 },
        { OFPACT_SET_IP_ECN, 8 },
        { OFPACT_SET_L4_SRC_PORT, 9 },
        { OFPACT_SET_L4_DST_PORT, 10 },
        /* OFPAT_COPY_TTL_OUT (11) not supported. */
        /* OFPAT_COPY_TTL_IN (12) not supported. */
        { OFPACT_SET_MPLS_LABEL, 13 },
        { OFPACT_SET_MPLS_TC, 14 },
        { OFPACT_SET_MPLS_TTL, 15 },
        { OFPACT_DEC_MPLS_TTL, 16 },
        { OFPACT_PUSH_VLAN, 17 },
        { OFPACT_STRIP_VLAN, 18 },
        { OFPACT_PUSH_MPLS, 19 },
        { OFPACT_POP_MPLS, 20 },
        { OFPACT_SET_QUEUE, 21 },
        { OFPACT_GROUP, 22 },
        { OFPACT_SET_IP_TTL, 23 },
        { OFPACT_DEC_TTL, 24 },
        { 0, -1 },
    };

    /* OpenFlow 1.2, 1.3, and 1.4 actions. */
    static const struct ofpact_map of12[] = {
        { OFPACT_OUTPUT, 0 },
        /* OFPAT_COPY_TTL_OUT (11) not supported. */
        /* OFPAT_COPY_TTL_IN (12) not supported. */
        { OFPACT_SET_MPLS_TTL, 15 },
        { OFPACT_DEC_MPLS_TTL, 16 },
        { OFPACT_PUSH_VLAN, 17 },
        { OFPACT_STRIP_VLAN, 18 },
        { OFPACT_PUSH_MPLS, 19 },
        { OFPACT_POP_MPLS, 20 },
        { OFPACT_SET_QUEUE, 21 },
        { OFPACT_GROUP, 22 },
        { OFPACT_SET_IP_TTL, 23 },
        { OFPACT_DEC_TTL, 24 },
        { OFPACT_SET_FIELD, 25 },
        /* OF1.3+ OFPAT_PUSH_PBB (26) not supported. */
        /* OF1.3+ OFPAT_POP_PBB (27) not supported. */
        { 0, -1 },
    };

    switch (version) {
    case OFP10_VERSION:
        return of10;

    case OFP11_VERSION:
        return of11;

    case OFP12_VERSION:
    case OFP13_VERSION:
    case OFP14_VERSION:
    case OFP15_VERSION:
    case OFP16_VERSION:
    default:
        return of12;
    }
}


check_DEC_TTL(const struct ofpact_cnt_ids *a OVS_UNUSED,
              struct ofpact_check_params *cp)
{
    return check_set_ip(cp);
}


format_SET_TUNNEL(const struct ofpact_tunnel *a,
                  const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
                  (a->tun_id > UINT32_MAX
                   || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""),
                  colors.end, a->tun_id);
}


format_PUSH_MPLS(const struct ofpact_push_mpls *a,
                 const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%spush_mpls:%s0x%04"PRIx16,
                  colors.param, colors.end, ntohs(a->ethertype));
}


check_STRIP_VLAN(const struct ofpact_null *a OVS_UNUSED,
                 struct ofpact_check_params *cp)
{
    if (!(cp->match->flow.vlans[0].tci & htons(VLAN_CFI))) {
        inconsistent_match(&cp->usable_protocols);
    }
    flow_pop_vlan(&cp->match->flow, NULL);
    return 0;
}


parse_CONTROLLER(char *arg, const struct ofpact_parse_params *pp)
{
    enum ofp_packet_in_reason reason = OFPR_ACTION;
    uint16_t controller_id = 0;
    uint16_t max_len = UINT16_MAX;
    uint32_t meter_id = NX_CTLR_NO_METER;
    const char *userdata = NULL;
    bool pause = false;

    if (!arg[0]) {
        /* Use defaults. */
    } else if (strspn(arg, "0123456789") == strlen(arg)) {
        char *error = str_to_u16(arg, "max_len", &max_len);
        if (error) {
            return error;
        }
    } else {
        char *name, *value;

        while (ofputil_parse_key_value(&arg, &name, &value)) {
            if (!strcmp(name, "reason")) {
                if (!ofputil_packet_in_reason_from_string(value, &reason)) {
                    return xasprintf("unknown reason \"%s\"", value);
                }
            } else if (!strcmp(name, "max_len")) {
                char *error = str_to_u16(value, "max_len", &max_len);
                if (error) {
                    return error;
                }
            } else if (!strcmp(name, "id")) {
                char *error = str_to_u16(value, "id", &controller_id);
                if (error) {
                    return error;
                }
            } else if (!strcmp(name, "userdata")) {
                userdata = value;
            } else if (!strcmp(name, "pause")) {
                pause = true;
            } else if (!strcmp(name, "meter_id")) {
                char *error = str_to_u32(value, &meter_id);
                if (error) {
                    return error;
                }
            } else {
                return xasprintf("unknown key \"%s\" parsing controller "
                                 "action", name);
            }
        }
    }

    if (reason == OFPR_ACTION && controller_id == 0 && !userdata && !pause
        && meter_id == NX_CTLR_NO_METER) {
        struct ofpact_output *output;

        output = ofpact_put_OUTPUT(pp->ofpacts);
        output->port = OFPP_CONTROLLER;
        output->max_len = max_len;
    } else {
        struct ofpact_controller *controller;

        controller = ofpact_put_CONTROLLER(pp->ofpacts);
        controller->max_len = max_len;
        controller->reason = reason;
        controller->controller_id = controller_id;
        controller->pause = pause;
        controller->meter_id = meter_id;

        if (userdata) {
            size_t start_ofs = pp->ofpacts->size;
            const char *end = ofpbuf_put_hex(pp->ofpacts, userdata, NULL);
            if (*end) {
                return xstrdup("bad hex digit in `controller' "
                               "action `userdata'");
            }
            size_t userdata_len = pp->ofpacts->size - start_ofs;
            controller = pp->ofpacts->header;
            controller->userdata_len = userdata_len;
        }
        ofpact_finish_CONTROLLER(pp->ofpacts, &controller);
    }

    return NULL;
}


format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%soutput%s(port=", colors.special, colors.end);
    ofputil_format_port(a->port, fp->port_map, fp->s);
    ds_put_format(fp->s, ",max_len=%"PRIu32")", a->max_len);
}


ofpacts_execute_action_set(struct ofpbuf *action_list,
                           const struct ofpbuf *action_set)
{
    const struct ofpact *slots[N_ACTION_SLOTS] = {NULL, };

    struct ofpbuf set_or_move;
    ofpbuf_init(&set_or_move, 0);

    const struct ofpact *final_action = NULL;
    enum action_set_class final_class = 0;

    const struct ofpact *cursor;
    OFPACT_FOR_EACH (cursor, action_set->data, action_set->size) {
        int class = action_set_classify(cursor);
        if (class < N_ACTION_SLOTS) {
            slots[class] = cursor;
        } else if (class < ACTION_SLOT_SET_OR_MOVE) {
            if (class >= final_class) {
                final_action = cursor;
                final_class = class;
            }
        } else if (class == ACTION_SLOT_SET_OR_MOVE) {
            ofpact_copy(&set_or_move, cursor);
        } else {
            ovs_assert(class == ACTION_SLOT_INVALID);
        }
    }

    if (final_action) {
        for (int i = 0; i < N_ACTION_SLOTS; i++) {
            if (slots[i]) {
                ofpact_copy(action_list, slots[i]);
            }
        }
        ofpbuf_put(action_list, set_or_move.data, set_or_move.size);
        ofpact_copy(action_list, final_action);
    }
    ofpbuf_uninit(&set_or_move);
}


encode_LEARN(const struct ofpact_learn *learn,
             enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    const struct ofpact_learn_spec *spec;
    struct nx_action_learn *nal;
    size_t start_ofs;

    start_ofs = out->size;

    if (learn->ofpact.raw == NXAST_RAW_LEARN2
        || learn->limit != 0
        || learn->flags & NX_LEARN_F_WRITE_RESULT) {
        struct nx_action_learn2 *nal2;

        nal2 = put_NXAST_LEARN2(out);
        nal2->limit = htonl(learn->limit);
        nal2->result_dst_ofs = htons(learn->result_dst.ofs);
        nal = &nal2->up;
    } else {
        nal = put_NXAST_LEARN(out);
    }
    nal->idle_timeout = htons(learn->idle_timeout);
    nal->hard_timeout = htons(learn->hard_timeout);
    nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
    nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
    nal->priority = htons(learn->priority);
    nal->cookie = learn->cookie;
    nal->flags = htons(learn->flags);
    nal->table_id = learn->table_id;

    if (learn->flags & NX_LEARN_F_WRITE_RESULT) {
        nx_put_header(out, learn->result_dst.field->id, 0, false);
    }

    OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) {
        put_u16(out, spec->n_bits | spec->dst_type | spec->src_type);

        if (spec->src_type == NX_LEARN_SRC_FIELD) {
            put_u32(out, nxm_header_from_mff(spec->src.field));
            put_u16(out, spec->src.ofs);
        } else {
            size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
            uint8_t *bits = ofpbuf_put_zeros(out, n_dst_bytes);
            unsigned int n_bytes = DIV_ROUND_UP(spec->n_bits, 8);

            memcpy(bits + n_dst_bytes - n_bytes, ofpact_learn_spec_imm(spec),
                   n_bytes);
        }

        if (spec->dst_type == NX_LEARN_DST_MATCH ||
            spec->dst_type == NX_LEARN_DST_LOAD) {
            put_u32(out, nxm_header_from_mff(spec->dst.field));
            put_u16(out, spec->dst.ofs);
        }
    }

    pad_ofpat(out, start_ofs);
}


parse_SET_TUNNEL(char *arg, const struct ofpact_parse_params *pp)
{
    return parse_set_tunnel(arg, NXAST_RAW_SET_TUNNEL, pp);
}


ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
{
    switch (ofpact->type) {
    case OFPACT_OUTPUT:
        return ofpact_get_OUTPUT(ofpact)->port == port;
    case OFPACT_ENQUEUE:
        return ofpact_get_ENQUEUE(ofpact)->port == port;
    case OFPACT_CONTROLLER:
        return port == OFPP_CONTROLLER;

    case OFPACT_OUTPUT_REG:
    case OFPACT_OUTPUT_TRUNC:
    case OFPACT_BUNDLE:
    case OFPACT_SET_VLAN_VID:
    case OFPACT_SET_VLAN_PCP:
    case OFPACT_STRIP_VLAN:
    case OFPACT_PUSH_VLAN:
    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_ETH_DST:
    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_IPV4_DST:
    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_L4_DST_PORT:
    case OFPACT_REG_MOVE:
    case OFPACT_SET_FIELD:
    case OFPACT_STACK_PUSH:
    case OFPACT_STACK_POP:
    case OFPACT_DEC_TTL:
    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_DEC_MPLS_TTL:
    case OFPACT_SET_TUNNEL:
    case OFPACT_WRITE_METADATA:
    case OFPACT_SET_QUEUE:
    case OFPACT_POP_QUEUE:
    case OFPACT_FIN_TIMEOUT:
    case OFPACT_RESUBMIT:
    case OFPACT_LEARN:
    case OFPACT_CONJUNCTION:
    case OFPACT_MULTIPATH:
    case OFPACT_NOTE:
    case OFPACT_EXIT:
    case OFPACT_UNROLL_XLATE:
    case OFPACT_PUSH_MPLS:
    case OFPACT_POP_MPLS:
    case OFPACT_SAMPLE:
    case OFPACT_CLEAR_ACTIONS:
    case OFPACT_CLONE:
    case OFPACT_WRITE_ACTIONS:
    case OFPACT_GOTO_TABLE:
    case OFPACT_METER:
    case OFPACT_GROUP:
    case OFPACT_DEBUG_RECIRC:
    case OFPACT_DEBUG_SLOW:
    case OFPACT_CT:
    case OFPACT_CT_CLEAR:
    case OFPACT_NAT:
    case OFPACT_ENCAP:
    case OFPACT_DECAP:
    case OFPACT_DEC_NSH_TTL:
    default:
        return false;
    }
}


parse_SET_MPLS_TTL(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(pp->ofpacts);

    if (*arg == '\0') {
        return xstrdup("set_mpls_ttl: expected ttl.");
    }

    mpls_ttl->ttl = atoi(arg);
    return NULL;
}


format_SET_VLAN_VID(const struct ofpact_vlan_vid *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%s%s:%s%"PRIu16, colors.param,
                  a->push_vlan_if_needed ? "mod_vlan_vid" : "set_vlan_vid",
                  colors.end, a->vlan_vid);
}


format_SET_IPV4_DST(const struct ofpact_ipv4 *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_nw_dst:%s"IP_FMT,
                  colors.param, colors.end, IP_ARGS(a->ipv4));
}


ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
              struct ofpact_check_params *cp)
{
    /* Save fields that might temporarily be modified. */
    struct flow *flow = &cp->match->flow;
    ovs_be32 packet_type = flow->packet_type;
    ovs_be16 dl_type = flow->dl_type;
    uint8_t nw_proto = flow->nw_proto;
    union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS];
    memcpy(vlans, flow->vlans, sizeof vlans);

    /* Check all the actions. */
    cp->usable_protocols = OFPUTIL_P_ANY;
    enum ofperr error = 0;
    struct ofpact *a;
    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
        error = ofpact_check__(a, cp);
        if (error) {
            break;
        }
    }

    /* Restore fields that may have been modified. */
    flow->packet_type = packet_type;
    flow->dl_type = dl_type;
    memcpy(flow->vlans, vlans, sizeof vlans);
    flow->nw_proto = nw_proto;

    return error;
}


parse_BUNDLE(const char *arg, const struct ofpact_parse_params *pp)
{
    return bundle_parse(arg, pp->port_map, pp->ofpacts);
}


ofpacts_parse(char *str, const struct ofpact_parse_params *pp,
              bool allow_instructions, enum ofpact_type outer_action)
{
    if (pp->depth >= MAX_OFPACT_PARSE_DEPTH) {
        return xstrdup("Action nested too deeply");
    }
    CONST_CAST(struct ofpact_parse_params *, pp)->depth++;
    uint32_t orig_size = pp->ofpacts->size;
    char *error = ofpacts_parse__(str, pp, allow_instructions, outer_action);
    if (error) {
        pp->ofpacts->size = orig_size;
    }
    CONST_CAST(struct ofpact_parse_params *, pp)->depth--;
    return error;
}


parse_POP_MPLS(char *arg, const struct ofpact_parse_params *pp)
{
    uint16_t ethertype;
    char *error;

    error = str_to_u16(arg, "pop_mpls", &ethertype);
    if (!error) {
        ofpact_put_POP_MPLS(pp->ofpacts)->ethertype = htons(ethertype);
    }
    return error;
}


check_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED,
                   const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


parse_STACK_POP(char *arg, const struct ofpact_parse_params *pp)
{
    return nxm_parse_stack_action(ofpact_put_STACK_POP(pp->ofpacts), arg);
}


action_set_classify(const struct ofpact *a)
{
    switch (a->type) {
#define SLOT(OFPACT) case OFPACT: return ACTION_SLOT_##OFPACT;
        ACTION_SET_ORDER
#undef SLOT

#define FINAL(OFPACT) case OFPACT: return ACTION_SLOT_##OFPACT;
        ACTION_SET_FINAL_PRIORITY
#undef FINAL

    case OFPACT_SET_FIELD:
    case OFPACT_REG_MOVE:
    case OFPACT_SET_ETH_DST:
    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_SET_IPV4_DST:
    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_L4_DST_PORT:
    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_SET_QUEUE:
    case OFPACT_SET_TUNNEL:
    case OFPACT_SET_VLAN_PCP:
    case OFPACT_SET_VLAN_VID:
        return ACTION_SLOT_SET_OR_MOVE;

    case OFPACT_BUNDLE:
    case OFPACT_CLEAR_ACTIONS:
    case OFPACT_CLONE:
    case OFPACT_NAT:
    case OFPACT_CONTROLLER:
    case OFPACT_ENQUEUE:
    case OFPACT_EXIT:
    case OFPACT_UNROLL_XLATE:
    case OFPACT_FIN_TIMEOUT:
    case OFPACT_GOTO_TABLE:
    case OFPACT_LEARN:
    case OFPACT_CONJUNCTION:
    case OFPACT_METER:
    case OFPACT_MULTIPATH:
    case OFPACT_NOTE:
    case OFPACT_OUTPUT_REG:
    case OFPACT_OUTPUT_TRUNC:
    case OFPACT_POP_QUEUE:
    case OFPACT_SAMPLE:
    case OFPACT_STACK_POP:
    case OFPACT_STACK_PUSH:
    case OFPACT_WRITE_ACTIONS:
    case OFPACT_WRITE_METADATA:
    case OFPACT_DEBUG_RECIRC:
    case OFPACT_DEBUG_SLOW:
        return ACTION_SLOT_INVALID;

    default:
        OVS_NOT_REACHED();
    }
}


parse_PUSH_VLAN(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_push_vlan *push_vlan;
    uint16_t ethertype;
    char *error;

    *pp->usable_protocols &= OFPUTIL_P_OF11_UP;
    error = str_to_u16(arg, "ethertype", &ethertype);
    if (error) {
        return error;
    }

    if (!eth_type_vlan(htons(ethertype))) {
        return xasprintf("%s: not a valid VLAN ethertype", arg);
    }
    push_vlan = ofpact_put_PUSH_VLAN(pp->ofpacts);
    push_vlan->ethertype = htons(ethertype);
    return NULL;
}


parse_CONJUNCTION(const char *arg, const struct ofpact_parse_params *pp)
{
    uint8_t n_clauses;
    uint8_t clause;
    uint32_t id;
    int n;

    if (!ovs_scan(arg, "%"SCNi32" , %"SCNu8" / %"SCNu8" %n",
                  &id, &clause, &n_clauses, &n) || n != strlen(arg)) {
        return xstrdup("\"conjunction\" syntax is \"conjunction(id,i/n)\"");
    }

    if (n_clauses < 2) {
        return xstrdup("conjunction must have at least 2 clauses");
    } else if (n_clauses > 64) {
        return xstrdup("conjunction must have at most 64 clauses");
    } else if (clause < 1) {
        return xstrdup("clause index must be positive");
    } else if (clause > n_clauses) {
        return xstrdup("clause index must be less than or equal to "
                       "number of clauses");
    }

    add_conjunction(pp->ofpacts, id, clause - 1, n_clauses);
    return NULL;
}


format_SET_L4_DST_PORT(const struct ofpact_l4_port *a,
                       const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_tp_dst:%s%d",
                  colors.param, colors.end, a->port);
}


decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf,
                             enum ofp_version ofp_version OVS_UNUSED,
                             const struct vl_mff_map *vl_mff_map,
                             uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    return decode_ofpat_set_field(oasf, false, vl_mff_map, tlv_bitmap,
                                  ofpacts);
}


decode_OFPAT_RAW10_OUTPUT(const struct ofp10_action_output *oao,
                          enum ofp_version ofp_version OVS_UNUSED,
                          struct ofpbuf *out)
{
    struct ofpact_output *output;

    output = ofpact_put_OUTPUT(out);
    output->port = u16_to_ofp(ntohs(oao->port));
    output->max_len = ntohs(oao->max_len);

    return ofpact_check_output_port(output->port, OFPP_MAX);
}


rgw: reject unauthenticated response-header actions

Signed-off-by: Matt Benjamin <mbenjamin@redhat.com>
Reviewed-by: Casey Bodley <cbodley@redhat.com>
(cherry picked from commit d8dd5e513c0c62bbd7d3044d7e2eddcd897bd400)

static int verify_mfa(RGWRados *store, RGWUserInfo *user, const string& mfa_str, bool *verified)
{
  vector<string> params;
  get_str_vec(mfa_str, " ", params);

  if (params.size() != 2) {
    ldout(store->ctx(), 5) << "NOTICE: invalid mfa string provided: " << mfa_str << dendl;
    return -EINVAL;
  }

  string& serial = params[0];
  string& pin = params[1];

  auto i = user->mfa_ids.find(serial);
  if (i == user->mfa_ids.end()) {
    ldout(store->ctx(), 5) << "NOTICE: user does not have mfa device with serial=" << serial << dendl;
    return -EACCES;
  }

  int ret = store->check_mfa(user->user_id, serial, pin);
  if (ret < 0) {
    ldout(store->ctx(), 20) << "NOTICE: failed to check MFA, serial=" << serial << dendl;
    return -EACCES;
  }

  *verified = true;

  return 0;
}

A flaw was found in the Ceph Object Gateway, where it supports request sent by an anonymous user in Amazon S3. This flaw could lead to potential XSS attacks due to the lack of proper neutralization of untrusted input.


RGWOp *RGWHandler_REST_Bucket_S3::op_get()
{
  if (s->info.args.sub_resource_exists("logging"))
    return new RGWGetBucketLogging_ObjStore_S3;

  if (s->info.args.sub_resource_exists("location"))
    return new RGWGetBucketLocation_ObjStore_S3;

  if (s->info.args.sub_resource_exists("versioning"))
    return new RGWGetBucketVersioning_ObjStore_S3;

  if (s->info.args.sub_resource_exists("website")) {
    if (!s->cct->_conf->rgw_enable_static_website) {
      return NULL;
    }
    return new RGWGetBucketWebsite_ObjStore_S3;
  }

  if (s->info.args.exists("mdsearch")) {
    return new RGWGetBucketMetaSearch_ObjStore_S3;
  }

  if (is_acl_op()) {
    return new RGWGetACLs_ObjStore_S3;
  } else if (is_cors_op()) {
    return new RGWGetCORS_ObjStore_S3;
  } else if (is_request_payment_op()) {
    return new RGWGetRequestPayment_ObjStore_S3;
  } else if (s->info.args.exists("uploads")) {
    return new RGWListBucketMultiparts_ObjStore_S3;
  } else if(is_lc_op()) {
    return new RGWGetLC_ObjStore_S3;
  } else if(is_policy_op()) {
    return new RGWGetBucketPolicy;
  }
  return get_obj_op(true);
}


void RGWGetACLs_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);
  rgw_flush_formatter(s, s->formatter);
  dump_body(s, acls);
}


void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
							  bool delete_marker,
							  const string& marker_version_id, int ret)
{
  if (!key.empty()) {
    if (op_ret == 0 && !quiet) {
      s->formatter->open_object_section("Deleted");
      s->formatter->dump_string("Key", key.name);
      if (!key.instance.empty()) {
	s->formatter->dump_string("VersionId", key.instance);
      }
      if (delete_marker) {
	s->formatter->dump_bool("DeleteMarker", true);
	s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
      }
      s->formatter->close_section();
    } else if (op_ret < 0) {
      struct rgw_http_error r;
      int err_no;

      s->formatter->open_object_section("Error");

      err_no = -op_ret;
      rgw_get_errno_s3(&r, err_no);

      s->formatter->dump_string("Key", key.name);
      s->formatter->dump_string("VersionId", key.instance);
      s->formatter->dump_string("Code", r.s3_code);
      s->formatter->dump_string("Message", r.s3_code);
      s->formatter->close_section();
    }

    rgw_flush_formatter(s, s->formatter);
  }
}


void RGWGetBucketLogging_ObjStore_S3::send_response()
{
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);

  s->formatter->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3);
  s->formatter->close_section();
  rgw_flush_formatter_and_reset(s, s->formatter);
}


void RGWGetBucketLocation_ObjStore_S3::send_response()
{
  dump_errno(s);
  end_header(s, this);
  dump_start(s);

  RGWZoneGroup zonegroup;
  string api_name;

  int ret = store->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
  if (ret >= 0) {
    api_name = zonegroup.api_name;
  } else  {
    if (s->bucket_info.zonegroup != "default") {
      api_name = s->bucket_info.zonegroup;
    }
  }

  s->formatter->dump_format_ns("LocationConstraint", XMLNS_AWS_S3,
			       "%s", api_name.c_str());
  rgw_flush_formatter_and_reset(s, s->formatter);
}


RGWOp* RGWHandler_REST_S3Website::op_head()
{
  return get_obj_op(false);
}


static void dump_usage_categories_info(Formatter *formatter, const rgw_usage_log_entry& entry, map<string, bool> *categories)
{
  formatter->open_array_section("categories");
  map<string, rgw_usage_data>::const_iterator uiter;
  for (uiter = entry.usage_map.begin(); uiter != entry.usage_map.end(); ++uiter) {
    if (categories && !categories->empty() && !categories->count(uiter->first))
      continue;
    const rgw_usage_data& usage = uiter->second;
    formatter->open_object_section("Entry");
    formatter->dump_string("Category", uiter->first);
    formatter->dump_int("BytesSent", usage.bytes_sent);
    formatter->dump_int("BytesReceived", usage.bytes_received);
    formatter->dump_int("Ops", usage.ops);
    formatter->dump_int("SuccessfulOps", usage.successful_ops);
    formatter->close_section(); // Entry
  }
  formatter->close_section(); // Category
}


AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
{
  return {
    s->auth.s3_postobj_creds.access_key,
    s->auth.s3_postobj_creds.signature,
    s->auth.s3_postobj_creds.encoded_policy.to_str(),
    rgw::auth::s3::get_v2_signature,
    null_completer_factory
  };
}


RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
{
  if (is_cors_op()) {
    return new RGWDeleteCORS_ObjStore_S3;
  } else if(is_lc_op()) {
    return new RGWDeleteLC_ObjStore_S3;
  } else if(is_policy_op()) {
    return new RGWDeleteBucketPolicy;
  }

  if (s->info.args.sub_resource_exists("website")) {
    if (!s->cct->_conf->rgw_enable_static_website) {
      return NULL;
    }
    return new RGWDeleteBucketWebsite_ObjStore_S3;
  }

  if (s->info.args.exists("mdsearch")) {
    return new RGWDelBucketMetaSearch_ObjStore_S3;
  }

  return new RGWDeleteBucket_ObjStore_S3;
}


void RGWGetBucketVersioning_ObjStore_S3::send_response()
{
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);

  s->formatter->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3);
  if (versioned) {
    const char *status = (versioning_enabled ? "Enabled" : "Suspended");
    s->formatter->dump_string("Status", status);
    const char *mfa_status = (mfa_enabled ? "Enabled" : "Disabled");
    s->formatter->dump_string("MfaDelete", mfa_status);
  }
  s->formatter->close_section();
  rgw_flush_formatter_and_reset(s, s->formatter);
}


void RGWPutLC_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);
}


void RGWInitMultipart_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  for (auto &it : crypt_http_responses)
     dump_header(s, it.first, it.second);
  end_header(s, this, "application/xml");
  if (op_ret == 0) {
    dump_start(s);
    s->formatter->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3);
    if (!s->bucket_tenant.empty())
      s->formatter->dump_string("Tenant", s->bucket_tenant);
    s->formatter->dump_string("Bucket", s->bucket_name);
    s->formatter->dump_string("Key", s->object.name);
    s->formatter->dump_string("UploadId", upload_id);
    s->formatter->close_section();
    rgw_flush_formatter_and_reset(s, s->formatter);
  }
}


  int get_request_payment_payer(bool *requester_pays) {
    XMLObj *config = find_first("RequestPaymentConfiguration");
    if (!config)
      return -EINVAL;

    *requester_pays = false;

    XMLObj *field = config->find_first("Payer");
    if (!field)
      return 0;

    string& s = field->get_data();

    if (stringcasecmp(s, "Requester") == 0) {
      *requester_pays = true;
    } else if (stringcasecmp(s, "BucketOwner") != 0) {
      return -EINVAL;
    }

    return 0;
  }


RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
{
  /** If we are in website mode, then it is explicitly impossible to run GET or
   * HEAD on the actual directory. We must convert the request to run on the
   * suffix object instead!
   */
  RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
  op->set_get_data(get_data);
  return op;
}


RGWOp *RGWHandler_REST_Service_S3::op_post()
{
  if (s->info.args.exists("Action")) {
    string action = s->info.args.get("Action");
    if (action.compare("CreateRole") == 0)
      return new RGWCreateRole;
    if (action.compare("DeleteRole") == 0)
      return new RGWDeleteRole;
    if (action.compare("GetRole") == 0)
      return new RGWGetRole;
    if (action.compare("UpdateAssumeRolePolicy") == 0)
      return new RGWModifyRole;
    if (action.compare("ListRoles") == 0)
      return new RGWListRoles;
    if (action.compare("PutRolePolicy") == 0)
      return new RGWPutRolePolicy;
    if (action.compare("GetRolePolicy") == 0)
      return new RGWGetRolePolicy;
    if (action.compare("ListRolePolicies") == 0)
      return new RGWListRolePolicies;
    if (action.compare("DeleteRolePolicy") == 0)
      return new RGWDeleteRolePolicy;
  }
  return NULL;
}


std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
{
  return content_type;
}


int RGW_Auth_S3::authorize(RGWRados* const store,
                           const rgw::auth::StrategyRegistry& auth_registry,
                           struct req_state* const s)
{

  /* neither keystone and rados enabled; warn and exit! */
  if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
      !store->ctx()->_conf->rgw_s3_auth_use_keystone &&
      !store->ctx()->_conf->rgw_s3_auth_use_ldap) {
    dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
    return -EPERM;
  }

  const auto ret = rgw::auth::Strategy::apply(auth_registry.get_s3_main(), s);
  if (ret == 0) {
    /* Populate the owner info. */
    s->owner.set_id(s->user->user_id);
    s->owner.set_name(s->user->display_name);
  }
  return ret;
}


RGWOp *RGWHandler_REST_Service_S3::op_get()
{
  if (is_usage_op()) {
    return new RGWGetUsage_ObjStore_S3;
  } else {
    return new RGWListBuckets_ObjStore_S3;
  }
}


int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetObj_Filter> *filter, RGWGetObj_Filter* cb, bufferlist* manifest_bl)
{
  if (skip_decrypt) { // bypass decryption for multisite sync requests
    return 0;
  }

  int res = 0;
  std::unique_ptr<BlockCrypt> block_crypt;
  res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
  if (res == 0) {
    if (block_crypt != nullptr) {
      auto f = std::make_unique<RGWGetObj_BlockDecrypt>(s->cct, cb, std::move(block_crypt));
      if (manifest_bl != nullptr) {
        res = f->read_manifest(*manifest_bl);
        if (res == 0) {
          *filter = std::move(f);
        }
      }
    }
  }
  return res;
}


int RGWSetRequestPayment_ObjStore_S3::get_params()
{
  char *data;
  int len = 0;
  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
  int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);

  if (r < 0) {
    return r;
  }

  RGWSetRequestPaymentParser parser;

  if (!parser.init()) {
    ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
    r = -EIO;
    goto done;
  }

  if (!parser.parse(data, len, 1)) {
    ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
    r = -EINVAL;
    goto done;
  }

  r = parser.get_request_payment_payer(&requester_pays);

done:
  free(data);

  return r;
}


int RGWPutCORS_ObjStore_S3::get_params()
{
  int r;
  char *data = nullptr;
  int len = 0;
  RGWCORSXMLParser_S3 parser(s->cct);
  RGWCORSConfiguration_S3 *cors_config;

  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
  r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
  if (r < 0) {
    return r;
  }

  auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};

  r = do_aws4_auth_completion();
  if (r < 0) {
    return r;
  }

  if (!parser.init()) {
    return -EINVAL;
  }

  if (!data || !parser.parse(data, len, 1)) {
    return -EINVAL;
  }
  cors_config =
    static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
					     "CORSConfiguration"));
  if (!cors_config) {
    return -EINVAL;
  }

#define CORS_RULES_MAX_NUM      100
  int max_num = s->cct->_conf->rgw_cors_rules_max_num;
  if (max_num < 0) {
    max_num = CORS_RULES_MAX_NUM;
  }
  int cors_rules_num = cors_config->get_rules().size();
  if (cors_rules_num > max_num) {
    ldout(s->cct, 4) << "An cors config can have up to "
                     << max_num
                     << " rules, request cors rules num: "
                     << cors_rules_num << dendl;
    op_ret = -ERR_INVALID_CORS_RULES_ERROR;
    s->err.message = "The number of CORS rules should not exceed allowed limit of "
                     + std::to_string(max_num) + " rules.";
    return -ERR_INVALID_REQUEST;
  }

  // forward bucket cors requests to meta master zone
  if (!store->is_meta_master()) {
    /* only need to keep this data around if we're not meta master */
    in_data.append(data, len);
  }

  if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
    ldout(s->cct, 15) << "CORSConfiguration";
    cors_config->to_xml(*_dout);
    *_dout << dendl;
  }

  cors_config->encode(cors_bl);

  return 0;
}


int RGWDeleteMultiObj_ObjStore_S3::get_params()
{
  int ret = RGWDeleteMultiObj_ObjStore::get_params();
  if (ret < 0) {
    return ret;
  }

  return do_aws4_auth_completion();
}


int RGWListBucket_ObjStore_S3::get_params()
{
  list_versions = s->info.args.exists("versions");
  prefix = s->info.args.get("prefix");
  if (!list_versions) {
    marker = s->info.args.get("marker");
  } else {
    marker.name = s->info.args.get("key-marker");
    marker.instance = s->info.args.get("version-id-marker");
  }

  // non-standard
  s->info.args.get_bool("allow-unordered", &allow_unordered, false);

  delimiter = s->info.args.get("delimiter");

  max_keys = s->info.args.get("max-keys");
  op_ret = parse_max_keys();
  if (op_ret < 0) {
    return op_ret;
  }

  encoding_type = s->info.args.get("encoding-type");
  if (s->system_request) {
    s->info.args.get_bool("objs-container", &objs_container, false);
    const char *shard_id_str = s->info.env->get("HTTP_RGWX_SHARD_ID");
    if (shard_id_str) {
      string err;
      shard_id = strict_strtol(shard_id_str, 10, &err);
      if (!err.empty()) {
        ldout(s->cct, 5) << "bad shard id specified: " << shard_id_str << dendl;
        return -EINVAL;
      }
    } else {
      shard_id = s->bucket_instance_shard_id;
    }
  }

  return 0;
}


int RGWHandler_Auth_S3::init(RGWRados *store, struct req_state *state,
                             rgw::io::BasicClient *cio)
{
  int ret = RGWHandler_REST_S3::init_from_header(state, RGW_FORMAT_JSON,
						     true);
  if (ret < 0)
    return ret;

  return RGWHandler_REST::init(store, state, cio);
}


rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
{
  /* The short form of "using" can't be used here -- we're aliasing a class'
   * member. */
  using acct_privilege_t = \
    rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;

  return rgw::auth::RemoteApplier::AuthInfo {
    rgw_user(token.id),
    token.id,
    RGW_PERM_FULL_CONTROL,
    acct_privilege_t::IS_PLAIN_ACCT,
    TYPE_LDAP
  };
}


int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
  map<string, bufferlist>::iterator iter;
  iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
  if (iter != attrs.end()) {
    bufferlist &bl = iter->second;
    s->redirect = bl.c_str();
    s->err.http_ret = 301;
    ldout(s->cct, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
    op_ret = -ERR_WEBSITE_REDIRECT;
    set_req_state_err(s, op_ret);
    dump_errno(s);
    dump_content_length(s, 0);
    dump_redirect(s, s->redirect);
    end_header(s, this);
    return op_ret;
  } else {
    return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
  }
}


discover_aws_flavour(const req_info& info)
{
  using rgw::auth::s3::AWS4_HMAC_SHA256_STR;

  AwsVersion version = AwsVersion::UNKNOWN;
  AwsRoute route = AwsRoute::UNKNOWN;

  const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
  if (http_auth && http_auth[0]) {
    /* Authorization in Header */
    route = AwsRoute::HEADERS;

    if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
                 strlen(AWS4_HMAC_SHA256_STR))) {
      /* AWS v4 */
      version = AwsVersion::V4;
    } else if (!strncmp(http_auth, "AWS ", 4)) {
      /* AWS v2 */
      version = AwsVersion::V2;
    }
  } else {
    route = AwsRoute::QUERY_STRING;

    if (info.args.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR) {
      /* AWS v4 */
      version = AwsVersion::V4;
    } else if (!info.args.get("AWSAccessKeyId").empty()) {
      /* AWS v2 */
      version = AwsVersion::V2;
    }
  }

  return std::make_pair(version, route);
}


int RGWGetObj_ObjStore_S3::get_params()
{
  // for multisite sync requests, only read the slo manifest itself, rather than
  // all of the data from its parts. the parts will sync as separate objects
  skip_manifest = s->info.args.exists(RGW_SYS_PARAM_PREFIX "sync-manifest");

  // multisite sync requests should fetch encrypted data, along with the
  // attributes needed to support decryption on the other zone
  if (s->system_request) {
    skip_decrypt = s->info.args.exists(RGW_SYS_PARAM_PREFIX "skip-decrypt");
  }

  return RGWGetObj_ObjStore::get_params();
}


int RGWHandler_REST_S3::postauth_init()
{
  struct req_init_state *t = &s->init_state;
  bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;

  rgw_parse_url_bucket(t->url_bucket, s->user->user_id.tenant,
		      s->bucket_tenant, s->bucket_name);

  dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
           << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;

  int ret;
  ret = rgw_validate_tenant_name(s->bucket_tenant);
  if (ret)
    return ret;
  if (!s->bucket_name.empty()) {
    ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
    if (ret)
      return ret;
    ret = validate_object_name(s->object.name);
    if (ret)
      return ret;
  }

  if (!t->src_bucket.empty()) {
    rgw_parse_url_bucket(t->src_bucket, s->user->user_id.tenant,
			s->src_tenant_name, s->src_bucket_name);
    ret = rgw_validate_tenant_name(s->src_tenant_name);
    if (ret)
      return ret;
    ret = valid_s3_bucket_name(s->src_bucket_name, relaxed_names);
    if (ret)
      return ret;
  }

  const char *mfa = s->info.env->get("HTTP_X_AMZ_MFA");
  if (mfa) {
    ret = verify_mfa(store, s->user, string(mfa), &s->mfa_verified);
  }

  return 0;
}


AWSEngine::authenticate(const req_state* const s) const
{
  /* Small reminder: an ver_abstractor is allowed to throw! */
  const auto auth_data = ver_abstractor.get_auth_data(s);

  if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
    return result_t::deny(-EINVAL);
  } else {
    return authenticate(auth_data.access_key_id,
		        auth_data.client_signature,
			auth_data.string_to_sign,
                        auth_data.signature_factory,
			auth_data.completer_factory,
			s);
  }
}


int RGWHandler_REST_S3Website::init(RGWRados *store, req_state *s,
                                    rgw::io::BasicClient* cio)
{
  // save the original object name before retarget() replaces it with the
  // result of get_effective_key(). the error_handler() needs the original
  // object name for redirect handling
  original_object_name = s->object.name;

  return RGWHandler_REST_S3::init(store, s, cio);
}


int RGWCopyObj_ObjStore_S3::get_params()
{
  if_mod = s->info.env->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
  if_unmod = s->info.env->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
  if_match = s->info.env->get("HTTP_X_AMZ_COPY_IF_MATCH");
  if_nomatch = s->info.env->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");

  src_tenant_name = s->src_tenant_name;
  src_bucket_name = s->src_bucket_name;
  src_object = s->src_object;
  dest_tenant_name = s->bucket.tenant;
  dest_bucket_name = s->bucket.name;
  dest_object = s->object.name;

  if (s->system_request) {
    source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
    s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", &copy_if_newer, false);
    if (!source_zone.empty()) {
      client_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "client-id");
      op_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "op-id");

      if (client_id.empty() || op_id.empty()) {
	ldout(s->cct, 0) <<
	  RGW_SYS_PARAM_PREFIX "client-id or "
	  RGW_SYS_PARAM_PREFIX "op-id were not provided, "
	  "required for intra-region copy"
			 << dendl;
	return -EINVAL;
      }
    }
  }

  copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
  auto tmp_md_d = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
  if (tmp_md_d) {
    if (strcasecmp(tmp_md_d, "COPY") == 0) {
      attrs_mod = RGWRados::ATTRSMOD_NONE;
    } else if (strcasecmp(tmp_md_d, "REPLACE") == 0) {
      attrs_mod = RGWRados::ATTRSMOD_REPLACE;
    } else if (!source_zone.empty()) {
      attrs_mod = RGWRados::ATTRSMOD_NONE; // default for intra-zone_group copy
    } else {
      s->err.message = "Unknown metadata directive.";
      ldout(s->cct, 0) << s->err.message << dendl;
      return -EINVAL;
    }
    md_directive = tmp_md_d;
  }

  if (source_zone.empty() &&
      (dest_tenant_name.compare(src_tenant_name) == 0) &&
      (dest_bucket_name.compare(src_bucket_name) == 0) &&
      (dest_object.compare(src_object.name) == 0) &&
      src_object.instance.empty() &&
      (attrs_mod != RGWRados::ATTRSMOD_REPLACE)) {
    /* can only copy object into itself if replacing attrs */
    s->err.message = "This copy request is illegal because it is trying to copy "
                     "an object to itself without changing the object's metadata, "
                     "storage class, website redirect location or encryption attributes.";
    ldout(s->cct, 0) << s->err.message << dendl;
    return -ERR_INVALID_REQUEST;
  }
  return 0;
}


void RGWDeleteLC_ObjStore_S3::send_response()
{
  if (op_ret == 0)
      op_ret = STATUS_NO_CONTENT;
  if (op_ret) {
    set_req_state_err(s, op_ret);
  }
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);
}


int RGWPutObjTags_ObjStore_S3::get_params()
{
  RGWObjTagsXMLParser parser;

  if (!parser.init()){
    return -EINVAL;
  }

  char *data=nullptr;
  int len=0;

  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
  int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);

  if (r < 0)
    return r;

  auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};

  if (!parser.parse(data, len, 1)) {
    return -ERR_MALFORMED_XML;
  }

  RGWObjTagSet_S3 *obj_tags_s3;
  RGWObjTagging_S3 *tagging;

  tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
  obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
  if(!obj_tags_s3){
    return -ERR_MALFORMED_XML;
  }

  RGWObjTags obj_tags;
  r = obj_tags_s3->rebuild(obj_tags);
  if (r < 0)
    return r;

  obj_tags.encode(tags_bl);
  ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;

  return 0;
}


void RGWPutCORS_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, NULL, "application/xml");
  dump_start(s);
}


void RGWDeleteObj_ObjStore_S3::send_response()
{
  int r = op_ret;
  if (r == -ENOENT)
    r = 0;
  if (!r)
    r = STATUS_NO_CONTENT;

  set_req_state_err(s, r);
  dump_errno(s);
  dump_header_if_nonempty(s, "x-amz-version-id", version_id);
  if (delete_marker) {
    dump_header(s, "x-amz-delete-marker", "true");
  }
  end_header(s, this);
}


void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
{
  if (op_ret == 0) {
    op_ret = STATUS_NO_CONTENT;
  }
  set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");
}


int RGWPutObj_ObjStore_S3::get_params()
{
  if (!s->length)
    return -ERR_LENGTH_REQUIRED;

  RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
  map<string, bufferlist> src_attrs;
  size_t pos;
  int ret;

  map_qs_metadata(s);

  RGWAccessControlPolicy_S3 s3policy(s->cct);
  ret = create_s3_policy(s, store, s3policy, s->owner);
  if (ret < 0)
    return ret;

  policy = s3policy;

  if_match = s->info.env->get("HTTP_IF_MATCH");
  if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
  copy_source = url_decode(s->info.env->get("HTTP_X_AMZ_COPY_SOURCE", ""));
  copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");

  /* handle x-amz-copy-source */
  boost::string_view cs_view(copy_source);
  if (! cs_view.empty()) {
    if (cs_view[0] == '/')
      cs_view.remove_prefix(1);
    copy_source_bucket_name = cs_view.to_string();
    pos = copy_source_bucket_name.find("/");
    if (pos == std::string::npos) {
      ret = -EINVAL;
      ldout(s->cct, 5) << "x-amz-copy-source bad format" << dendl;
      return ret;
    }
    copy_source_object_name =
      copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
    copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
#define VERSION_ID_STR "?versionId="
    pos = copy_source_object_name.find(VERSION_ID_STR);
    if (pos == std::string::npos) {
      copy_source_object_name = url_decode(copy_source_object_name);
    } else {
      copy_source_version_id =
	copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
      copy_source_object_name =
	url_decode(copy_source_object_name.substr(0, pos));
    }
    pos = copy_source_bucket_name.find(":");
    if (pos == std::string::npos) {
       copy_source_tenant_name = s->src_tenant_name;
    } else {
       copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
       copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
       if (copy_source_bucket_name.empty()) {
         ret = -EINVAL;
         ldout(s->cct, 5) << "source bucket name is empty" << dendl;
         return ret;
       }
    }
    ret = store->get_bucket_info(obj_ctx,
                                 copy_source_tenant_name,
                                 copy_source_bucket_name,
                                 copy_source_bucket_info,
                                 NULL, &src_attrs);
    if (ret < 0) {
      ldout(s->cct, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
      return ret;
    }

    /* handle x-amz-copy-source-range */

    if (copy_source_range) {
      string range = copy_source_range;
      pos = range.find("=");
      if (pos == std::string::npos) {
        ret = -EINVAL;
        ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
        return ret;
      }
      range = range.substr(pos + 1);
      pos = range.find("-");
      if (pos == std::string::npos) {
        ret = -EINVAL;
        ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
        return ret;
      }
      string first = range.substr(0, pos);
      string last = range.substr(pos + 1);
      copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
      copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
    }

  } /* copy_source */

  /* handle object tagging */
  auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
  if (tag_str){
    obj_tags = std::make_unique<RGWObjTags>();
    ret = obj_tags->set_from_string(tag_str);
    if (ret < 0){
      ldout(s->cct,0) << "setting obj tags failed with " << ret << dendl;
      if (ret == -ERR_INVALID_TAG){
        ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
      }

      return ret;
    }
  }

  return RGWPutObj_ObjStore::get_params();
}


AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
{
  const boost::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;

  /* grab access key id */
  const size_t pos = credential.find("/");
  const boost::string_view access_key_id = credential.substr(0, pos);
  dout(10) << "access key id = " << access_key_id << dendl;

  /* grab credential scope */
  const boost::string_view credential_scope = credential.substr(pos + 1);
  dout(10) << "credential scope = " << credential_scope << dendl;

  const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
                                     credential_scope,
                                     std::placeholders::_1,
                                     std::placeholders::_2,
                                     std::placeholders::_3);

  return {
    access_key_id,
    s->auth.s3_postobj_creds.signature,
    s->auth.s3_postobj_creds.encoded_policy.to_str(),
    sig_factory,
    null_completer_factory
  };
}


int RGWPutObj_ObjStore_S3::get_decrypt_filter(
    std::unique_ptr<RGWGetObj_Filter>* filter,
    RGWGetObj_Filter* cb,
    map<string, bufferlist>& attrs,
    bufferlist* manifest_bl)
{
  std::map<std::string, std::string> crypt_http_responses_unused;

  int res = 0;
  std::unique_ptr<BlockCrypt> block_crypt;
  res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
  if (res == 0) {
    if (block_crypt != nullptr) {
      auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
      //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
      if (f != nullptr) {
        if (manifest_bl != nullptr) {
          res = f->read_manifest(*manifest_bl);
          if (res == 0) {
            *filter = std::move(f);
          }
        }
      }
    }
  }
  return res;
}


int RGWPutObj_ObjStore_S3::get_encrypt_filter(
    std::unique_ptr<RGWPutObjDataProcessor>* filter,
    RGWPutObjDataProcessor* cb)
{
  int res = 0;
  RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast<RGWPutObjProcessor_Multipart*>(cb);
  if (multi_processor != nullptr) {
    RGWMPObj* mp = nullptr;
    multi_processor->get_mp(&mp);
    if (mp != nullptr) {
      map<string, bufferlist> xattrs;
      string meta_oid;
      meta_oid = mp->get_meta();

      rgw_obj obj;
      obj.init_ns(s->bucket, meta_oid, RGW_OBJ_NS_MULTIPART);
      obj.set_in_extra_data(true);
      res = get_obj_attrs(store, s, obj, xattrs);
      if (res == 0) {
        std::unique_ptr<BlockCrypt> block_crypt;
        /* We are adding to existing object.
         * We use crypto mode that configured as if we were decrypting. */
        res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
        if (res == 0 && block_crypt != nullptr)
          *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
              new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
      }
    }
    /* it is ok, to not have encryption at all */
  }
  else
  {
    std::unique_ptr<BlockCrypt> block_crypt;
    res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
    if (res == 0 && block_crypt != nullptr) {
      *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
          new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
    }
  }
  return res;
}


int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados *store,
						  struct req_state *s,
						  stringstream& ss)
{
  RGWAccessControlPolicy_S3 s3policy(s->cct);

  // bucket-* canned acls do not apply to bucket
  if (s->object.empty()) {
    if (s->canned_acl.find("bucket") != string::npos)
      s->canned_acl.clear();
  }

  int r = create_s3_policy(s, store, s3policy, owner);
  if (r < 0)
    return r;

  s3policy.to_xml(ss);

  return 0;
}


int RGWGetUsage_ObjStore_S3::get_params()
{
  start_date = s->info.args.get("start-date");
  end_date = s->info.args.get("end-date");
  return 0;
}


int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
  int ret = 0;
  s->formatter->reset(); /* Try to throw it all away */

  std::shared_ptr<RGWGetObj_ObjStore_S3Website> getop( static_cast<RGWGetObj_ObjStore_S3Website*>(op_get()));
  if (getop.get() == NULL) {
    return -1; // Trigger double error handler
  }
  getop->init(store, s, this);
  getop->range_str = NULL;
  getop->if_mod = NULL;
  getop->if_unmod = NULL;
  getop->if_match = NULL;
  getop->if_nomatch = NULL;
  s->object = errordoc_key;

  ret = init_permissions(getop.get());
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  ret = read_permissions(getop.get());
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  if (http_ret) {
     getop->set_custom_http_response(http_ret);
  }

  ret = getop->init_processing();
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  ret = getop->verify_op_mask();
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  ret = getop->verify_permission();
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  ret = getop->verify_params();
  if (ret < 0) {
    ldout(s->cct, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
    return -1; // Trigger double error handler
  }

  // No going back now
  getop->pre_exec();
  /*
   * FIXME Missing headers:
   * With a working errordoc, the s3 error fields are rendered as HTTP headers,
   *   x-amz-error-code: NoSuchKey
   *   x-amz-error-message: The specified key does not exist.
   *   x-amz-error-detail-Key: foo
   */
  getop->execute();
  getop->complete();
  return 0;

}


void RGWPutObjTags_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);

}


void RGWListBuckets_ObjStore_S3::send_response_end()
{
  if (sent_data) {
    s->formatter->close_section();
    list_all_buckets_end(s);
    rgw_flush_formatter_and_reset(s, s->formatter);
  }
}


AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
                                       const bool using_qs) const
{
  boost::string_view access_key_id;
  boost::string_view signed_hdrs;

  boost::string_view date;
  boost::string_view credential_scope;
  boost::string_view client_signature;

  int ret = rgw::auth::s3::parse_v4_credentials(s->info,
						access_key_id,
						credential_scope,
						signed_hdrs,
						client_signature,
						date,
						using_qs);
  if (ret < 0) {
    throw ret;
  }

  /* craft canonical headers */
  boost::optional<std::string> canonical_headers = \
    get_v4_canonical_headers(s->info, signed_hdrs, using_qs);
  if (canonical_headers) {
    using sanitize = rgw::crypt_sanitize::log_content;
    ldout(s->cct, 10) << "canonical headers format = "
                      << sanitize{*canonical_headers} << dendl;
  } else {
    throw -EPERM;
  }

  /* Get the expected hash. */
  auto exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);

  /* Craft canonical URI. Using std::move later so let it be non-const. */
  auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);

  /* Craft canonical query string. std::moving later so non-const here. */
  auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs);

  /* Craft canonical request. */
  auto canonical_req_hash = \
    rgw::auth::s3::get_v4_canon_req_hash(s->cct,
                                         s->info.method,
                                         std::move(canonical_uri),
                                         std::move(canonical_qs),
                                         std::move(*canonical_headers),
                                         signed_hdrs,
                                         exp_payload_hash);

  auto string_to_sign = \
    rgw::auth::s3::get_v4_string_to_sign(s->cct,
                                         AWS4_HMAC_SHA256_STR,
                                         date,
                                         credential_scope,
                                         std::move(canonical_req_hash));

  const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
                                     credential_scope,
                                     std::placeholders::_1,
                                     std::placeholders::_2,
                                     std::placeholders::_3);

  /* Requests authenticated with the Query Parameters are treated as unsigned.
   * From "Authenticating Requests: Using Query Parameters (AWS Signature
   * Version 4)":
   *
   *   You don't include a payload hash in the Canonical Request, because
   *   when you create a presigned URL, you don't know the payload content
   *   because the URL is used to upload an arbitrary payload. Instead, you
   *   use a constant string UNSIGNED-PAYLOAD.
   *
   * This means we have absolutely no business in spawning completer. Both
   * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
   * by default. We don't need to change that. */
  if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s)) {
    return {
      access_key_id,
      client_signature,
      std::move(string_to_sign),
      sig_factory,
      null_completer_factory
    };
  } else {
    /* We're going to handle a signed payload. Be aware that even empty HTTP
     * body (no payload) requires verification:
     *
     *   The x-amz-content-sha256 header is required for all AWS Signature
     *   Version 4 requests. It provides a hash of the request payload. If
     *   there is no payload, you must provide the hash of an empty string. */
    if (!is_v4_payload_streamed(exp_payload_hash)) {
      ldout(s->cct, 10) << "delaying v4 auth" << dendl;

      /* payload in a single chunk */
      switch (s->op_type)
      {
        case RGW_OP_CREATE_BUCKET:
        case RGW_OP_PUT_OBJ:
        case RGW_OP_PUT_ACLS:
        case RGW_OP_PUT_CORS:
        case RGW_OP_INIT_MULTIPART: // in case that Init Multipart uses CHUNK encoding
        case RGW_OP_COMPLETE_MULTIPART:
        case RGW_OP_SET_BUCKET_VERSIONING:
        case RGW_OP_DELETE_MULTI_OBJ:
        case RGW_OP_ADMIN_SET_METADATA:
        case RGW_OP_SET_BUCKET_WEBSITE:
        case RGW_OP_PUT_BUCKET_POLICY:
        case RGW_OP_PUT_OBJ_TAGGING:
        case RGW_OP_PUT_LC:
        case RGW_OP_SET_REQUEST_PAYMENT:
          break;
        default:
          dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
          throw -ERR_NOT_IMPLEMENTED;
      }

      const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
                                          s,
                                          std::placeholders::_1);
      return {
        access_key_id,
        client_signature,
        std::move(string_to_sign),
        sig_factory,
        cmpl_factory
      };
    } else {
      /* IMHO "streamed" doesn't fit too good here. I would prefer to call
       * it "chunked" but let's be coherent with Amazon's terminology. */

      dout(10) << "body content detected in multiple chunks" << dendl;

      /* payload in multiple chunks */

      switch(s->op_type)
      {
        case RGW_OP_PUT_OBJ:
          break;
        default:
          dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
          throw -ERR_NOT_IMPLEMENTED;
      }

      dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;

      /* In the case of streamed payload client sets the x-amz-content-sha256
       * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
       * when constructing the Canonical Request. */

      /* In the case of single-chunk upload client set the header's value is
       * coherent with the one used for Canonical Request crafting. */

      /* In the case of query string-based authentication there should be no
       * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
       * for CanonReq. */
      const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
                                          s,
                                          date,
                                          credential_scope,
                                          client_signature,
                                          std::placeholders::_1);
      return {
        access_key_id,
        client_signature,
        std::move(string_to_sign),
        sig_factory,
        cmpl_factory
      };
    }
  }
}


RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data)
{
  // Non-website mode
  if (get_data) {
    return new RGWListBucket_ObjStore_S3;
  } else {
    return new RGWStatBucket_ObjStore_S3;
  }
}


void RGWOptionsCORS_ObjStore_S3::send_response()
{
  string hdrs, exp_hdrs;
  uint32_t max_age = CORS_MAX_AGE_INVALID;
  /*EACCES means, there is no CORS registered yet for the bucket
   *ENOENT means, there is no match of the Origin in the list of CORSRule
   */
  if (op_ret == -ENOENT)
    op_ret = -EACCES;
  if (op_ret < 0) {
    set_req_state_err(s, op_ret);
    dump_errno(s);
    end_header(s, NULL);
    return;
  }
  get_response_params(hdrs, exp_hdrs, &max_age);

  dump_errno(s);
  dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
		      max_age);
  end_header(s, NULL);
}


RGWOp *RGWHandler_REST_Service_S3::op_head()
{
  return new RGWListBuckets_ObjStore_S3;
}


rgw::auth::s3::LDAPEngine::get_acl_strategy() const
{
  //This is based on the assumption that the default acl strategy in
  // get_perms_from_aclspec, will take care. Extra acl spec is not required.
  return nullptr;
}


void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
{
  if (! sent_header) {
    if (op_ret)
    set_req_state_err(s, op_ret);
    dump_errno(s);

    // Explicitly use chunked transfer encoding so that we can stream the result
    // to the user without having to wait for the full length of it.
    end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
    dump_start(s);
    if (op_ret == 0) {
      s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
    }
    sent_header = true;
  } else {
    /* Send progress field. Note that this diverge from the original S3
     * spec. We do this in order to keep connection alive.
     */
    s->formatter->dump_int("Progress", (uint64_t)ofs);
  }
  rgw_flush_formatter(s, s->formatter);
}


void RGWDeleteMultiObj_ObjStore_S3::begin_response()
{

  if (!status_dumped) {
    send_status();
  }

  dump_start(s);
  // Explicitly use chunked transfer encoding so that we can stream the result
  // to the user without having to wait for the full length of it.
  end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
  s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);

  rgw_flush_formatter(s, s->formatter);
}


RGWOp *RGWHandler_REST_Obj_S3::op_delete()
{
  if (is_tagging_op()) {
    return new RGWDeleteObjTags_ObjStore_S3;
  }
  string upload_id = s->info.args.get("uploadId");

  if (upload_id.empty())
    return new RGWDeleteObj_ObjStore_S3;
  else
    return new RGWAbortMultipart_ObjStore_S3;
}


RGWOp *RGWHandler_REST_Bucket_S3::op_put()
{
  if (s->info.args.sub_resource_exists("logging"))
    return NULL;
  if (s->info.args.sub_resource_exists("versioning"))
    return new RGWSetBucketVersioning_ObjStore_S3;
  if (s->info.args.sub_resource_exists("website")) {
    if (!s->cct->_conf->rgw_enable_static_website) {
      return NULL;
    }
    return new RGWSetBucketWebsite_ObjStore_S3;
  }
  if (is_acl_op()) {
    return new RGWPutACLs_ObjStore_S3;
  } else if (is_cors_op()) {
    return new RGWPutCORS_ObjStore_S3;
  } else if (is_request_payment_op()) {
    return new RGWSetRequestPayment_ObjStore_S3;
  } else if(is_lc_op()) {
    return new RGWPutLC_ObjStore_S3;
  } else if(is_policy_op()) {
    return new RGWPutBucketPolicy;
  }
  return new RGWCreateBucket_ObjStore_S3;
}


void RGWGetBucketWebsite_ObjStore_S3::send_response()
{
  if (op_ret)
    set_req_state_err(s, op_ret);
  dump_errno(s);
  end_header(s, this, "application/xml");
  dump_start(s);

  if (op_ret < 0) {
    return;
  }

  RGWBucketWebsiteConf& conf = s->bucket_info.website_conf;

  s->formatter->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3);
  conf.dump_xml(s->formatter);
  s->formatter->close_section(); // WebsiteConfiguration
  rgw_flush_formatter_and_reset(s, s->formatter);
}