Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: pol_stats.c,v 1.2 2026/05/09 18:49:16 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	pol_stats 3
      6 /* SUMMARY
      7 /*	manage per-feature policy compliance status
      8 /* SYNOPSIS
      9 /*	#include <pol_stats.h>
     10 /*
     11 /*	POL_STATS *pol_stats_create(void)
     12 /*
     13 /*	void	pol_stats_revert(POL_STATS *pstats)
     14 /*
     15 /*	void	pol_stats_free(POL_STATS *pstats)
     16 /*
     17 /*	void	pol_stat_activate(
     18 /*	POL_STATS *pstats,
     19 /*	int	idx,
     20 /*	const char *init_name)
     21 /*
     22 /*	void	pol_stat_decide(
     23 /*	POL_STATS *pstats,
     24 /*	int	idx,
     25 /*	const char *final_name,
     26 /*	int	status)
     27 /*
     28 /*	int	pol_stats_used(POL_STATS *pstats)
     29 /*
     30 /*	pol_stats_format(
     31 /*	VSTRING *buf,
     32 /*	const POL_STATS *pstats)
     33 /* DESCRIPTION
     34 /*	This module records for each activated feature whether the
     35 /*	current program state satisfies the policy requirements for that
     36 /*	feature. For example, whether a TLS handshake result matches
     37 /*	DANE or STS requirements. The combined feature state can
     38 /*	concisely be formatted with pol_stats_format() and exposed
     39 /*	with logging.
     40 /*
     41 /*	Each feature has an initial name with the desired policy state,
     42 /*	and a final name that corresponds to the policy state that was
     43 /*	actually achieved. If the two names differ, then both names will
     44 /*	be logged as initial:final.
     45 /*
     46 /*	pol_stats_create() creates one POL_STATS instance with all status
     47 /*	information set to POL_STAT_INACTIVE.
     48 /*
     49 /*	pol_stats_revert() reverts all changes after pol_stats_create().
     50 /*
     51 /*	pol_stats_free() recycles storage for a POL_STATS instance.
     52 /*
     53 /*	Specific status information is accessed with an index. Valid
     54 /*	indices are 0 or 1 (the caller decides their purpose).
     55 /*
     56 /*	pol_stat_activate() changes the status in pstats at index idx
     57 /*	from POL_STAT_INACTIVE to POL_STAT_UNDECIDED, and updates the
     58 /*	feature's initial name (pointer copy). Calls with an invalid
     59 /*	index result in a panic(), and calls with an already active
     60 /*	index result in a warning.
     61 /*
     62 /*	pol_stat_decide() updates the status in pstats at index idx from
     63 /*	POL_STAT_UNDECIDED to POL_STAT_COMPLIANT or POL_STAT_VIOLATION,
     64 /*	and records its final name or NULL (pointer copy). Calls with
     65 /*	an invalid index or an unexpected decision status result in
     66 /*	a panic(), and calls with an inactive or already decided index
     67 /*	status result in a warning.
     68 /*
     69 /*	pol_stats_used() returns the number of activated categories for
     70 /*	its argument.
     71 /*
     72 /*	pol_stats_format() formats TLS feature status information as
     73 /*	feature/feature/etc., where:
     74 /* .IP \(bu
     75 /*	Each feature name is the initial name given to
     76 /*	pol_stat_activate().
     77 /* .IP \(bu
     78 /*	When ':final-name' is appended to a feature name, the feature
     79 /*	was downgraded to the final name given to pol_stats_decide().
     80 /*	This does not necessarily imply policy compliance or
     81 /*	non-compliance for that feature.
     82 /* .IP \(bu
     83 /*	When "!" is prepended to the feature, the policy for that feature
     84 /*	was not satisfied and the operation was blocked.
     85 /* .IP \(bu
     86 /*	When "?" is appended to the feature, the policy for that feature
     87 /*	was left undecided. This typically caused by a lost connection.
     88 /* LICENSE
     89 /* .ad
     90 /* .fi
     91 /*	The Secure Mailer license must be distributed with this software.
     92 /* AUTHOR(S)
     93 /*	Wietse Venema
     94 /*	porcupine.org
     95 /*--*/
     96 
     97 #ifdef USE_TLS
     98 
     99  /*
    100   * System library.
    101   */
    102 #include <sys_defs.h>
    103 #include <string.h>
    104 
    105  /*
    106   * Utility library.
    107   */
    108 #include <msg.h>
    109 #include <mymalloc.h>
    110 
    111  /*
    112   * Global library.
    113   */
    114 #include <pol_stats.h>
    115 
    116 /* pol_stats_create - create an all-inactive POL_STATS instance */
    117 
    118 POL_STATS *pol_stats_create(void)
    119 {
    120     POL_STATS *pstats;
    121     POL_STAT *tp;
    122 
    123 #define POL_STAT_INIT(tp) do { \
    124 	(tp)->init_name = 0; \
    125 	(tp)->final_name = 0; \
    126 	(tp)->status = POL_STAT_INACTIVE; \
    127     } while (0);
    128 
    129     pstats = (POL_STATS *) mymalloc(sizeof(*pstats));
    130     pstats->used = 0;
    131     for (tp = pstats->st; tp < pstats->st + POL_STATS_SIZE; tp++)
    132 	POL_STAT_INIT(tp);
    133     return pstats;
    134 }
    135 
    136 /* pol_stats_revert - revert changes after pol_stats_create() */
    137 
    138 void    pol_stats_revert(POL_STATS *pstats)
    139 {
    140     POL_STAT *tp;
    141 
    142     pstats->used = 0;
    143     for (tp = pstats->st; tp < pstats->st + POL_STATS_SIZE; tp++)
    144 	if (tp->status != POL_STAT_INACTIVE)
    145 	    POL_STAT_INIT(tp);
    146 }
    147 
    148 /* pol_stats_free - POL_STATS destructor */
    149 
    150 void    pol_stats_free(POL_STATS *pstats)
    151 {
    152     myfree(pstats);
    153 }
    154 
    155 /* pol_stat_activate - activate status at index */
    156 
    157 void    pol_stat_activate(POL_STATS *pstats, int idx, const char *init_name)
    158 {
    159     POL_STAT *pol_stat;
    160 
    161     if (idx < 0 || idx >= POL_STATS_SIZE)
    162 	msg_panic("%s: bad index: %d", __func__, idx);
    163     pol_stat = pstats->st + idx;
    164     if (pol_stat->status != POL_STAT_INACTIVE)
    165 	msg_warn("%s: already active POL_STAT at index %d", __func__, idx);
    166     pol_stat->init_name = init_name;
    167     pol_stat->final_name = 0;
    168     pol_stat->status = POL_STAT_UNDECIDED;
    169     pstats->used += 1;
    170 }
    171 
    172 /* pol_stat_decide - update undecided status at index */
    173 
    174 extern void pol_stat_decide(POL_STATS *pstats, int idx, const char *final_name,
    175 			            int status)
    176 {
    177     POL_STAT *pol_stat;
    178 
    179     if (status != POL_STAT_VIOLATION && status != POL_STAT_COMPLIANT)
    180 	msg_panic("%s: bad new status: %d", __func__, status);
    181     if (idx < 0 || idx >= POL_STATS_SIZE)
    182 	msg_panic("%s: bad index: %d", __func__, idx);
    183     pol_stat = pstats->st + idx;
    184     if (pol_stat->status != POL_STAT_UNDECIDED)
    185 	msg_warn("%s: unexpected status %d at index %d",
    186 		 __func__, pol_stat->status, idx);
    187     pol_stat->final_name = final_name;
    188     pol_stat->status = status;
    189 }
    190 
    191 /* pol_stat_access - peek at specific POL_STAT instance. */
    192 
    193 static const POL_STAT *pol_stat_access(const POL_STATS *pstats, int idx)
    194 {
    195     const POL_STAT *pol_stat;
    196 
    197     if (idx < 0 || idx >= POL_STATS_SIZE)
    198 	msg_panic("%s: bad index: %d", __func__, idx);
    199     pol_stat = pstats->st + idx;
    200     return (pol_stat);
    201 }
    202 
    203 /* pol_stats_format - render in external representation */
    204 
    205 void    pol_stats_format(VSTRING *buf, const POL_STATS *pstats)
    206 {
    207     int     idx;
    208     const POL_STAT *pstat;
    209     int     field_count = 0;
    210 
    211     for (idx = 0; idx < POL_STATS_SIZE; idx++) {
    212 	pstat = pol_stat_access(pstats, idx);
    213 	if (pstat->status == POL_STAT_INACTIVE)
    214 	    continue;
    215 	if (field_count > 0)
    216 	    vstring_strcat(buf, "/");
    217 	if (pstat->status == POL_STAT_VIOLATION)
    218 	    vstring_strcat(buf, "!");
    219 	vstring_strcat(buf, pstat->init_name);
    220 	if (pstat->final_name != 0
    221 	    && strcmp(pstat->init_name, pstat->final_name) != 0)
    222 	    vstring_sprintf_append(buf, ":%s", (pstat)->final_name);
    223 	if (pstat->status == POL_STAT_UNDECIDED)
    224 	    vstring_strcat(buf, "?");
    225 	field_count += 1;
    226     }
    227 }
    228 
    229 #endif					/* USE_TLS */
    230