1 /* 2 * Logging functions for PAM modules. 3 * 4 * Logs errors and debugging messages from PAM modules. The debug versions 5 * only log anything if debugging was enabled; the crit and err versions 6 * always log. 7 * 8 * The canonical version of this file is maintained in the rra-c-util package, 9 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>. 10 * 11 * Written by Russ Allbery <eagle@eyrie.org> 12 * Copyright 2015, 2018, 2020 Russ Allbery <eagle@eyrie.org> 13 * Copyright 2005-2007, 2009-2010, 2012-2013 14 * The Board of Trustees of the Leland Stanford Junior University 15 * 16 * Permission is hereby granted, free of charge, to any person obtaining a 17 * copy of this software and associated documentation files (the "Software"), 18 * to deal in the Software without restriction, including without limitation 19 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 * and/or sell copies of the Software, and to permit persons to whom the 21 * Software is furnished to do so, subject to the following conditions: 22 * 23 * The above copyright notice and this permission notice shall be included in 24 * all copies or substantial portions of the Software. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 29 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 32 * DEALINGS IN THE SOFTWARE. 33 * 34 * SPDX-License-Identifier: MIT 35 */ 36 37 #include <config.h> 38 #ifdef HAVE_KRB5 39 # include <portable/krb5.h> 40 #endif 41 #include <portable/pam.h> 42 #include <portable/system.h> 43 44 #include <syslog.h> 45 46 #include <pam-util/args.h> 47 #include <pam-util/logging.h> 48 49 #ifndef LOG_AUTHPRIV 50 # define LOG_AUTHPRIV LOG_AUTH 51 #endif 52 53 /* Used for iterating through arrays. */ 54 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) 55 56 /* 57 * Mappings of PAM flags to symbolic names for logging when entering a PAM 58 * module function. 59 */ 60 static const struct { 61 int flag; 62 const char *name; 63 } FLAGS[] = { 64 /* clang-format off */ 65 {PAM_CHANGE_EXPIRED_AUTHTOK, "expired" }, 66 {PAM_DELETE_CRED, "delete" }, 67 {PAM_DISALLOW_NULL_AUTHTOK, "nonull" }, 68 {PAM_ESTABLISH_CRED, "establish"}, 69 {PAM_PRELIM_CHECK, "prelim" }, 70 {PAM_REFRESH_CRED, "refresh" }, 71 {PAM_REINITIALIZE_CRED, "reinit" }, 72 {PAM_SILENT, "silent" }, 73 {PAM_UPDATE_AUTHTOK, "update" }, 74 /* clang-format on */ 75 }; 76 77 78 /* 79 * Utility function to format a message into newly allocated memory, reporting 80 * an error via syslog if vasprintf fails. 81 */ 82 static char *__attribute__((__format__(printf, 1, 0))) 83 format(const char *fmt, va_list args) 84 { 85 char *msg; 86 87 if (vasprintf(&msg, fmt, args) < 0) { 88 syslog(LOG_CRIT | LOG_AUTHPRIV, "vasprintf failed: %m"); 89 return NULL; 90 } 91 return msg; 92 } 93 94 95 /* 96 * Log wrapper function that adds the user. Log a message with the given 97 * priority, prefixed by (user <user>) with the account name being 98 * authenticated if known. 99 */ 100 static void __attribute__((__format__(printf, 3, 0))) 101 log_vplain(struct pam_args *pargs, int priority, const char *fmt, va_list args) 102 { 103 char *msg; 104 105 if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug)) 106 return; 107 if (pargs != NULL && pargs->user != NULL) { 108 msg = format(fmt, args); 109 if (msg == NULL) 110 return; 111 pam_syslog(pargs->pamh, priority, "(user %s) %s", pargs->user, msg); 112 free(msg); 113 } else if (pargs != NULL) { 114 pam_vsyslog(pargs->pamh, priority, fmt, args); 115 } else { 116 msg = format(fmt, args); 117 if (msg == NULL) 118 return; 119 syslog(priority | LOG_AUTHPRIV, "%s", msg); 120 free(msg); 121 } 122 } 123 124 125 /* 126 * Wrapper around log_vplain with variadic arguments. 127 */ 128 static void __attribute__((__format__(printf, 3, 4))) 129 log_plain(struct pam_args *pargs, int priority, const char *fmt, ...) 130 { 131 va_list args; 132 133 va_start(args, fmt); 134 log_vplain(pargs, priority, fmt, args); 135 va_end(args); 136 } 137 138 139 /* 140 * Log wrapper function for reporting a PAM error. Log a message with the 141 * given priority, prefixed by (user <user>) with the account name being 142 * authenticated if known, followed by a colon and the formatted PAM error. 143 * However, do not include the colon and the PAM error if the PAM status is 144 * PAM_SUCCESS. 145 */ 146 static void __attribute__((__format__(printf, 4, 0))) 147 log_pam(struct pam_args *pargs, int priority, int status, const char *fmt, 148 va_list args) 149 { 150 char *msg; 151 152 if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug)) 153 return; 154 msg = format(fmt, args); 155 if (msg == NULL) 156 return; 157 if (pargs == NULL) 158 log_plain(NULL, priority, "%s", msg); 159 else if (status == PAM_SUCCESS) 160 log_plain(pargs, priority, "%s", msg); 161 else 162 log_plain(pargs, priority, "%s: %s", msg, 163 pam_strerror(pargs->pamh, status)); 164 free(msg); 165 } 166 167 168 /* 169 * The public interfaces. For each common log level (crit, err, and debug), 170 * generate a putil_<level> function and one for _pam. Do this with the 171 * preprocessor to save duplicate code. 172 */ 173 /* clang-format off */ 174 #define LOG_FUNCTION(level, priority) \ 175 void __attribute__((__format__(printf, 2, 3))) \ 176 putil_ ## level(struct pam_args *pargs, const char *fmt, ...) \ 177 { \ 178 va_list args; \ 179 \ 180 va_start(args, fmt); \ 181 log_vplain(pargs, priority, fmt, args); \ 182 va_end(args); \ 183 } \ 184 void __attribute__((__format__(printf, 3, 4))) \ 185 putil_ ## level ## _pam(struct pam_args *pargs, int status, \ 186 const char *fmt, ...) \ 187 { \ 188 va_list args; \ 189 \ 190 va_start(args, fmt); \ 191 log_pam(pargs, priority, status, fmt, args); \ 192 va_end(args); \ 193 } 194 LOG_FUNCTION(crit, LOG_CRIT) 195 LOG_FUNCTION(err, LOG_ERR) 196 LOG_FUNCTION(notice, LOG_NOTICE) 197 LOG_FUNCTION(debug, LOG_DEBUG) 198 /* clang-format on */ 199 200 201 /* 202 * Report entry into a function. Takes the PAM arguments, the function name, 203 * and the flags and maps the flags to symbolic names. 204 */ 205 void 206 putil_log_entry(struct pam_args *pargs, const char *func, int flags) 207 { 208 size_t i, length, offset; 209 char *out = NULL, *nout; 210 211 if (!pargs->debug) 212 return; 213 if (flags != 0) 214 for (i = 0; i < ARRAY_SIZE(FLAGS); i++) { 215 if (!(flags & FLAGS[i].flag)) 216 continue; 217 if (out == NULL) { 218 out = strdup(FLAGS[i].name); 219 if (out == NULL) 220 break; 221 } else { 222 length = strlen(FLAGS[i].name); 223 nout = realloc(out, strlen(out) + length + 2); 224 if (nout == NULL) { 225 free(out); 226 out = NULL; 227 break; 228 } 229 out = nout; 230 offset = strlen(out); 231 out[offset] = '|'; 232 memcpy(out + offset + 1, FLAGS[i].name, length); 233 out[offset + 1 + length] = '\0'; 234 } 235 } 236 if (out == NULL) 237 pam_syslog(pargs->pamh, LOG_DEBUG, "%s: entry", func); 238 else { 239 pam_syslog(pargs->pamh, LOG_DEBUG, "%s: entry (%s)", func, out); 240 free(out); 241 } 242 } 243 244 245 /* 246 * Report an authentication failure. This is a separate function since we 247 * want to include various PAM metadata in the log message and put it in a 248 * standard format. The format here is modeled after the pam_unix 249 * authentication failure message from Linux PAM. 250 */ 251 void __attribute__((__format__(printf, 2, 3))) 252 putil_log_failure(struct pam_args *pargs, const char *fmt, ...) 253 { 254 char *msg; 255 va_list args; 256 const char *ruser = NULL; 257 const char *rhost = NULL; 258 const char *tty = NULL; 259 const char *name = NULL; 260 261 if (pargs->user != NULL) 262 name = pargs->user; 263 va_start(args, fmt); 264 msg = format(fmt, args); 265 va_end(args); 266 if (msg == NULL) 267 return; 268 pam_get_item(pargs->pamh, PAM_RUSER, (PAM_CONST void **) &ruser); 269 pam_get_item(pargs->pamh, PAM_RHOST, (PAM_CONST void **) &rhost); 270 pam_get_item(pargs->pamh, PAM_TTY, (PAM_CONST void **) &tty); 271 272 /* clang-format off */ 273 pam_syslog(pargs->pamh, LOG_NOTICE, "%s; logname=%s uid=%ld euid=%ld" 274 " tty=%s ruser=%s rhost=%s", msg, 275 (name != NULL) ? name : "", 276 (long) getuid(), (long) geteuid(), 277 (tty != NULL) ? tty : "", 278 (ruser != NULL) ? ruser : "", 279 (rhost != NULL) ? rhost : ""); 280 /* clang-format on */ 281 282 free(msg); 283 } 284 285 286 /* 287 * Below are the additional logging functions enabled if built with Kerberos 288 * support, used to report Kerberos errors. 289 */ 290 #ifdef HAVE_KRB5 291 292 293 /* 294 * Log wrapper function for reporting a Kerberos error. Log a message with 295 * the given priority, prefixed by (user <user>) with the account name being 296 * authenticated if known, followed by a colon and the formatted Kerberos 297 * error. 298 */ 299 __attribute__((__format__(printf, 4, 0))) static void 300 log_krb5(struct pam_args *pargs, int priority, int status, const char *fmt, 301 va_list args) 302 { 303 char *msg; 304 const char *k5_msg = NULL; 305 306 if (priority == LOG_DEBUG && (pargs == NULL || !pargs->debug)) 307 return; 308 msg = format(fmt, args); 309 if (msg == NULL) 310 return; 311 if (pargs != NULL && pargs->ctx != NULL) { 312 k5_msg = krb5_get_error_message(pargs->ctx, status); 313 log_plain(pargs, priority, "%s: %s", msg, k5_msg); 314 } else { 315 log_plain(pargs, priority, "%s", msg); 316 } 317 free(msg); 318 if (k5_msg != NULL) 319 krb5_free_error_message(pargs->ctx, k5_msg); 320 } 321 322 323 /* 324 * The public interfaces. Do this with the preprocessor to save duplicate 325 * code. 326 */ 327 /* clang-format off */ 328 #define LOG_FUNCTION_KRB5(level, priority) \ 329 void __attribute__((__format__(printf, 3, 4))) \ 330 putil_ ## level ## _krb5(struct pam_args *pargs, int status, \ 331 const char *fmt, ...) \ 332 { \ 333 va_list args; \ 334 \ 335 va_start(args, fmt); \ 336 log_krb5(pargs, priority, status, fmt, args); \ 337 va_end(args); \ 338 } 339 LOG_FUNCTION_KRB5(crit, LOG_CRIT) 340 LOG_FUNCTION_KRB5(err, LOG_ERR) 341 LOG_FUNCTION_KRB5(notice, LOG_NOTICE) 342 LOG_FUNCTION_KRB5(debug, LOG_DEBUG) 343 /* clang-format on */ 344 345 #endif /* HAVE_KRB5 */ 346