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