| File: | src/usr.sbin/npppd/npppd/chap.c |
| Warning: | line 734, column 2 Value stored to 'lpkt' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: chap.c,v 1.17 2021/03/29 03:54:39 yasuoka Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 2009 Internet Initiative Japan Inc. |
| 5 | * All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * 1. Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in the |
| 14 | * documentation and/or other materials provided with the distribution. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 26 | * SUCH DAMAGE. |
| 27 | */ |
| 28 | /**@file |
| 29 | * This file provides CHAP (PPP Challenge Handshake Authentication Protocol, |
| 30 | * RFC 1994) handlers. Currently this contains authenticator side |
| 31 | * implementation only. |
| 32 | *<p> |
| 33 | * Supported authentication types: |
| 34 | * <li>MD5-CHAP</li> |
| 35 | * <li>MS-CHAP Version 2 (RFC 2759)</li> |
| 36 | * </ul></p> |
| 37 | */ |
| 38 | /* RFC 1994, 2433 */ |
| 39 | /* $Id: chap.c,v 1.17 2021/03/29 03:54:39 yasuoka Exp $ */ |
| 40 | #include <sys/types.h> |
| 41 | #include <sys/socket.h> |
| 42 | #include <sys/time.h> |
| 43 | #include <netinet/in.h> |
| 44 | #include <net/if_dl.h> |
| 45 | #include <stdlib.h> |
| 46 | #include <stdio.h> |
| 47 | #include <syslog.h> |
| 48 | #include <string.h> |
| 49 | #include <unistd.h> |
| 50 | #include <stdarg.h> |
| 51 | #include <errno(*__errno()).h> |
| 52 | #include <time.h> |
| 53 | #include <event.h> |
| 54 | #include <md5.h> |
| 55 | #include <vis.h> |
| 56 | |
| 57 | #include "npppd.h" |
| 58 | #include "ppp.h" |
| 59 | |
| 60 | #ifdef USE_NPPPD_RADIUS1 |
| 61 | #include "radius_chap_const.h" |
| 62 | #include "npppd_radius.h" |
| 63 | #endif |
| 64 | #include "npppd_defs.h" |
| 65 | |
| 66 | #include "debugutil.h" |
| 67 | #include "chap_ms.h" |
| 68 | |
| 69 | #define HEADERLEN4 4 |
| 70 | |
| 71 | #define CHAP_STATE_INITIAL1 1 |
| 72 | #define CHAP_STATE_SENT_CHALLENGE2 2 |
| 73 | #define CHAP_STATE_AUTHENTICATING3 3 |
| 74 | #define CHAP_STATE_SENT_RESPONSE4 4 |
| 75 | #define CHAP_STATE_STOPPED5 5 |
| 76 | #define CHAP_STATE_PROXY_AUTHENTICATION6 6 |
| 77 | |
| 78 | /* retry intervals */ |
| 79 | #define CHAP_TIMEOUT3 3 |
| 80 | #define CHAP_RETRY10 10 |
| 81 | |
| 82 | #define CHAP_CHALLENGE1 1 |
| 83 | #define CHAP_RESPONSE2 2 |
| 84 | #define CHAP_SUCCESS3 3 |
| 85 | #define CHAP_FAILURE4 4 |
| 86 | |
| 87 | /* from RFC 2433 */ |
| 88 | #define ERROR_RESTRICTED_LOGIN_HOURS646 646 |
| 89 | #define ERROR_ACCT_DISABLED647 647 |
| 90 | #define ERROR_PASSWD_EXPIRED648 648 |
| 91 | #define ERROR_NO_DIALIN_PERMISSION649 649 |
| 92 | #define ERROR_AUTHENTICATION_FAILURE691 691 |
| 93 | #define ERROR_CHANGING_PASSWORD709 709 |
| 94 | |
| 95 | /* MprError.h */ |
| 96 | #define ERROR_AUTH_SERVER_TIMEOUT930 930 |
| 97 | |
| 98 | #ifdef CHAP_DEBUG |
| 99 | #define CHAP_DBG(x) chap_log x |
| 100 | #define CHAP_ASSERT(cond) \ |
| 101 | if (!(cond)) { \ |
| 102 | fprintf(stderr(&__sF[2]), \ |
| 103 | "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ |
| 104 | , __func__, __FILE__"/usr/src/usr.sbin/npppd/npppd/chap.c", __LINE__104); \ |
| 105 | abort(); \ |
| 106 | } |
| 107 | #else |
| 108 | #define CHAP_ASSERT(cond) |
| 109 | #define CHAP_DBG(x) |
| 110 | #endif |
| 111 | |
| 112 | static void chap_authenticate(chap *_this, u_char *, int); |
| 113 | static void chap_failure(chap *, const char *, int); |
| 114 | static void chap_response (chap *, int, u_char *, int); |
| 115 | static void chap_create_challenge (chap *); |
| 116 | static void chap_send_error (chap *, const char *); |
| 117 | static void md5chap_authenticate (chap *, int, char *, u_char *, int, u_char *); |
| 118 | static void mschapv2_send_error (chap *, int, int); |
| 119 | static void mschapv2_authenticate (chap *, int, char *, u_char *, int, u_char *); |
| 120 | #ifdef USE_NPPPD_RADIUS1 |
| 121 | static void chap_radius_authenticate (chap *, int, char *, u_char *, int, u_char *); |
| 122 | static void chap_radius_response (void *, RADIUS_PACKET *, int, RADIUS_REQUEST_CTX); |
| 123 | #endif |
| 124 | static char *strip_nt_domain (char *); |
| 125 | static void chap_log (chap *, uint32_t, const char *, ...) __printflike(3,4)__attribute__((__format__ (__printf__, 3, 4))); |
| 126 | |
| 127 | /** Initialize the CHAP */ |
| 128 | void |
| 129 | chap_init(chap *_this, npppd_ppp *ppp) |
| 130 | { |
| 131 | struct tunnconf *conf; |
| 132 | |
| 133 | CHAP_ASSERT(ppp != NULL); |
| 134 | CHAP_ASSERT(_this != NULL); |
| 135 | |
| 136 | memset(_this, 0, sizeof(chap)); |
| 137 | _this->ppp = ppp; |
| 138 | |
| 139 | conf = ppp_get_tunnconf(ppp); |
| 140 | |
| 141 | if (conf->chap_name == NULL((void *)0)) |
| 142 | gethostname(_this->myname, sizeof(_this->myname)); |
| 143 | else |
| 144 | strlcpy(_this->myname, conf->chap_name, sizeof(_this->myname)); |
| 145 | |
| 146 | _this->timerctx.ctx = _this; |
| 147 | _this->state = CHAP_STATE_INITIAL1; |
| 148 | |
| 149 | _this->ntry = CHAP_RETRY10; |
| 150 | } |
| 151 | |
| 152 | /** Start CHAP as a authenticator. Send a challenge */ |
| 153 | void |
| 154 | chap_start(chap *_this) |
| 155 | { |
| 156 | u_char *challp, *challp0; |
| 157 | int lmyname; |
| 158 | |
| 159 | CHAP_ASSERT(_this != NULL); |
| 160 | CHAP_ASSERT(_this->ppp != NULL); |
| 161 | |
| 162 | if (_this->state == CHAP_STATE_PROXY_AUTHENTICATION6) { |
| 163 | _this->type = PPP_AUTH_CHAP_MD50x05; |
| 164 | _this->state = CHAP_STATE_AUTHENTICATING3; |
| 165 | chap_authenticate(_this, _this->ppp->proxy_authen_resp, |
| 166 | _this->ppp->lproxy_authen_resp); |
| 167 | return; |
| 168 | } |
| 169 | |
| 170 | if (_this->state == CHAP_STATE_INITIAL1 || |
| 171 | _this->state == CHAP_STATE_SENT_CHALLENGE2) { |
| 172 | if (_this->ntry > 0) { |
| 173 | _this->ntry--; |
| 174 | _this->type = _this->ppp->peer_auth; |
| 175 | |
| 176 | /* The type is supported? */ |
| 177 | if (_this->type != PPP_AUTH_CHAP_MS_V20x81 && |
| 178 | _this->type != PPP_AUTH_CHAP_MD50x05) { |
| 179 | chap_log(_this, LOG_ALERT1, |
| 180 | "Requested authentication type(0x%x) " |
| 181 | "is not supported.", _this->type); |
| 182 | ppp_set_disconnect_cause(_this->ppp, |
| 183 | PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, |
| 184 | PPP_PROTO_CHAP0xC223, 2 /* local */, NULL((void *)0)); |
| 185 | ppp_stop(_this->ppp, "Authentication Required"); |
| 186 | return; |
| 187 | } |
| 188 | |
| 189 | |
| 190 | #ifdef USE_NPPPD_MPPE1 |
| 191 | /* The peer must use MS-CHAP-V2 as the type */ |
| 192 | if (MPPE_IS_REQUIRED(_this->ppp)(((_this->ppp)->mppe.enabled != 0) && ((_this-> ppp)->mppe.required != 0)) && |
| 193 | _this->type != PPP_AUTH_CHAP_MS_V20x81) { |
| 194 | chap_log(_this, LOG_ALERT1, |
| 195 | "mppe is required but try to start chap " |
| 196 | "type=0x%02x", _this->type); |
| 197 | ppp_set_disconnect_cause(_this->ppp, |
| 198 | PPP_DISCON_AUTH_PROTOCOL_UNACCEPTABLE, |
| 199 | PPP_PROTO_CHAP0xC223, 2 /* local */, NULL((void *)0)); |
| 200 | ppp_stop(_this->ppp, "Authentication Required"); |
| 201 | return; |
| 202 | } |
| 203 | #endif |
| 204 | /* Generate a challenge packet and send it */ |
| 205 | challp = ppp_packetbuf(_this->ppp, PPP_AUTH_CHAP0xc223); |
| 206 | challp += HEADERLEN4; |
| 207 | challp0 = challp; |
| 208 | |
| 209 | chap_create_challenge(_this); |
| 210 | |
| 211 | PUTCHAR(_this->lchall, challp){ *(challp)++ = (u_char) (_this->lchall); }; |
| 212 | memcpy(challp, &_this->chall, _this->lchall); |
| 213 | challp += _this->lchall; |
| 214 | |
| 215 | lmyname = strlen(_this->myname); |
| 216 | |
| 217 | memcpy(challp, _this->myname, lmyname); |
| 218 | challp += lmyname; |
| 219 | |
| 220 | _this->challid = ++_this->pktid; |
| 221 | |
| 222 | ppp_output(_this->ppp, PPP_PROTO_CHAP0xC223, CHAP_CHALLENGE1, |
| 223 | _this->challid, challp0, challp - challp0); |
| 224 | |
| 225 | _this->state = CHAP_STATE_SENT_CHALLENGE2; |
| 226 | |
| 227 | TIMEOUT((void (*)(void *))chap_start, _this,{ struct timeval tv0; tv0.tv_usec = 0; tv0.tv_sec = (3); if ( !((&(_this)->timerctx.ev)->ev_flags & 0x80)) event_set (&(_this)->timerctx.ev, -1, 0, fsm_evtimer_timeout, & (_this)->timerctx); (_this)->timerctx.func = (void (*)( void *))chap_start; event_del(&(_this)->timerctx.ev); event_add (&(_this)->timerctx.ev, &tv0); } |
| 228 | CHAP_TIMEOUT){ struct timeval tv0; tv0.tv_usec = 0; tv0.tv_sec = (3); if ( !((&(_this)->timerctx.ev)->ev_flags & 0x80)) event_set (&(_this)->timerctx.ev, -1, 0, fsm_evtimer_timeout, & (_this)->timerctx); (_this)->timerctx.func = (void (*)( void *))chap_start; event_del(&(_this)->timerctx.ev); event_add (&(_this)->timerctx.ev, &tv0); }; |
| 229 | } else { |
| 230 | chap_log(_this, LOG_INFO6, |
| 231 | "Client did't respond our challenage."); |
| 232 | ppp_set_disconnect_cause(_this->ppp, |
| 233 | PPP_DISCON_AUTH_FSM_TIMEOUT, |
| 234 | PPP_PROTO_CHAP0xC223, 0, NULL((void *)0)); |
| 235 | ppp_stop(_this->ppp, "Authentication Required"); |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | /** Stop the CHAP */ |
| 241 | void |
| 242 | chap_stop(chap *_this) |
| 243 | { |
| 244 | _this->state = CHAP_STATE_STOPPED5; |
| 245 | UNTIMEOUT(chap_start, _this)event_del(&(_this)->timerctx.ev); |
| 246 | #ifdef USE_NPPPD_RADIUS1 |
| 247 | if (_this->radctx != NULL((void *)0)) { |
| 248 | radius_cancel_request(_this->radctx); |
| 249 | _this->radctx = NULL((void *)0); |
| 250 | } |
| 251 | #endif |
| 252 | } |
| 253 | |
| 254 | /** Called when a CHAP packet is received. */ |
| 255 | void |
| 256 | chap_input(chap *_this, u_char *pktp, int len) |
| 257 | { |
| 258 | int code, id, length, lval, lname, authok; |
| 259 | u_char *pktp1, *val, namebuf[MAX_USERNAME_LENGTH256]; |
| 260 | char *name; |
| 261 | |
| 262 | if (_this->state == CHAP_STATE_STOPPED5 || |
| 263 | _this->state == CHAP_STATE_INITIAL1) { |
| 264 | chap_log(_this, LOG_INFO6, "Received chap packet. But chap is " |
| 265 | "not started"); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | CHAP_ASSERT(_this != NULL); |
| 270 | if (len < 4) { |
| 271 | chap_log(_this, LOG_ERR3, "%s: Received broken packet.", |
| 272 | __func__); |
| 273 | return; |
| 274 | } |
| 275 | |
| 276 | pktp1 = pktp; |
| 277 | |
| 278 | GETCHAR(code, pktp1){ (code) = *(pktp1)++; }; |
| 279 | GETCHAR(id, pktp1){ (id) = *(pktp1)++; }; |
| 280 | GETSHORT(length, pktp1){ (length) = *(pktp1)++ << 8; (length) |= *(pktp1)++; }; |
| 281 | if (len < length || len < 5) { |
| 282 | chap_log(_this, LOG_ERR3, "%s: Received broken packet.", |
| 283 | __func__); |
| 284 | return; |
| 285 | } |
| 286 | |
| 287 | if (code != CHAP_RESPONSE2) { |
| 288 | chap_log(_this, LOG_ERR3, "Received unknown code=%d", code); |
| 289 | return; |
| 290 | } |
| 291 | |
| 292 | /* Create a chap response */ |
| 293 | |
| 294 | if (id != _this->challid) { |
| 295 | chap_log(_this, LOG_ERR3, |
| 296 | "Received challenge response has unknown id."); |
| 297 | return; |
| 298 | } |
| 299 | if (_this->state == CHAP_STATE_AUTHENTICATING3) |
| 300 | return; |
| 301 | |
| 302 | authok = 0; |
| 303 | UNTIMEOUT(chap_start, _this)event_del(&(_this)->timerctx.ev); |
| 304 | |
| 305 | /* pick the username */ |
| 306 | GETCHAR(lval, pktp1){ (lval) = *(pktp1)++; }; |
| 307 | val = pktp1; |
| 308 | pktp1 += lval; |
| 309 | |
| 310 | if (lval > length) { |
| 311 | chap_log(_this, LOG_ERR3, |
| 312 | "Received challenge response has invalid Value-Size " |
| 313 | "field. %d", lval); |
| 314 | return; |
| 315 | } |
| 316 | name = pktp1; |
| 317 | lname = len - (pktp1 - pktp); |
| 318 | if (lname <= 0 || sizeof(namebuf) <= lname + 1) { |
| 319 | chap_log(_this, LOG_ERR3, |
| 320 | "Received challenge response has invalid Name " |
| 321 | "field."); |
| 322 | return; |
| 323 | } |
| 324 | memcpy(namebuf, name, lname); |
| 325 | namebuf[lname] = '\0'; |
| 326 | name = namebuf; |
| 327 | if (_this->state == CHAP_STATE_SENT_RESPONSE4) { |
| 328 | if (strcmp(_this->name, name) != 0) { |
| 329 | /* |
| 330 | * The peer requests us to resend, but the username |
| 331 | * has been changed. |
| 332 | */ |
| 333 | chap_log(_this, LOG_ERR3, |
| 334 | "Received AuthReq is not same as before. " |
| 335 | "%s != %s", name, _this->name); |
| 336 | return; |
| 337 | } |
| 338 | } else if (_this->state != CHAP_STATE_SENT_CHALLENGE2) { |
| 339 | chap_log(_this, LOG_ERR3, |
| 340 | "Received AuthReq in illegal state. username=%s", name); |
| 341 | return; |
| 342 | } |
| 343 | _this->state = CHAP_STATE_AUTHENTICATING3; |
| 344 | strlcpy(_this->name, name, sizeof(_this->name)); |
| 345 | |
| 346 | chap_authenticate(_this, val, lval); |
| 347 | } |
| 348 | |
| 349 | static void |
| 350 | chap_failure(chap *_this, const char *msg, int mschapv2err) |
| 351 | { |
| 352 | |
| 353 | switch(_this->type) { |
| 354 | case PPP_AUTH_CHAP_MD50x05: |
| 355 | chap_send_error(_this, "FAILED"); |
| 356 | break; |
| 357 | case PPP_AUTH_CHAP_MS_V20x81: |
| 358 | mschapv2_send_error(_this, mschapv2err, 0); |
| 359 | break; |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | static void |
| 364 | chap_authenticate(chap *_this, u_char *response, int lresponse) |
| 365 | { |
| 366 | |
| 367 | switch(_this->type) { |
| 368 | case PPP_AUTH_CHAP_MD50x05: |
| 369 | /* check the length */ |
| 370 | if (lresponse != 16) { |
| 371 | chap_log(_this, LOG_ERR3, |
| 372 | "Invalid response length %d != 16", lresponse); |
| 373 | chap_failure(_this, "FAILED", |
| 374 | ERROR_AUTHENTICATION_FAILURE691); |
| 375 | return; |
| 376 | } |
| 377 | break; |
| 378 | case PPP_AUTH_CHAP_MS_V20x81: |
| 379 | /* check the length */ |
| 380 | if (lresponse < 49) { |
| 381 | chap_log(_this, LOG_ERR3, "Packet too short."); |
| 382 | chap_failure(_this, "FAILED", |
| 383 | ERROR_AUTHENTICATION_FAILURE691); |
| 384 | return; |
| 385 | } |
| 386 | break; |
| 387 | } |
| 388 | if (npppd_ppp_bind_realm(_this->ppp->pppd, _this->ppp, _this->name, 0) |
| 389 | == 0) { |
| 390 | if (!npppd_ppp_is_realm_ready(_this->ppp->pppd, _this->ppp)) { |
| 391 | chap_log(_this, LOG_INFO6, |
| 392 | "username=\"%s\" realm is not ready.", _this->name); |
| 393 | chap_failure(_this, "FAILED", |
| 394 | ERROR_AUTH_SERVER_TIMEOUT930); |
| 395 | return; |
| 396 | } |
| 397 | #ifdef USE_NPPPD_RADIUS1 |
| 398 | if (npppd_ppp_is_realm_radius(_this->ppp->pppd, _this->ppp)) { |
| 399 | chap_radius_authenticate(_this, _this->challid, |
| 400 | _this->name, _this->chall, _this->lchall, response); |
| 401 | return; |
| 402 | /* NOTREACHED */ |
| 403 | } else |
| 404 | #endif |
| 405 | if (npppd_ppp_is_realm_local(_this->ppp->pppd, _this->ppp)) { |
| 406 | switch(_this->type) { |
| 407 | case PPP_AUTH_CHAP_MD50x05: |
| 408 | md5chap_authenticate(_this, _this->challid, |
| 409 | _this->name, _this->chall, _this->lchall, |
| 410 | response); |
| 411 | return; |
| 412 | /* NOTREACHED */ |
| 413 | case PPP_AUTH_CHAP_MS_V20x81: |
| 414 | mschapv2_authenticate(_this, _this->challid, |
| 415 | strip_nt_domain(_this->name), |
| 416 | _this->chall, _this->lchall, response); |
| 417 | return; |
| 418 | /* NOTREACHED */ |
| 419 | } |
| 420 | } |
| 421 | } |
| 422 | chap_failure(_this, "FAILED", ERROR_AUTHENTICATION_FAILURE691); |
| 423 | |
| 424 | return; |
| 425 | } |
| 426 | |
| 427 | static void |
| 428 | chap_response(chap *_this, int authok, u_char *pktp, int lpktp) |
| 429 | { |
| 430 | const char *realm_name; |
| 431 | |
| 432 | CHAP_ASSERT(_this != NULL); |
| 433 | CHAP_ASSERT(pktp != NULL); |
| 434 | CHAP_ASSERT(_this->type == PPP_AUTH_CHAP_MD5 || |
| 435 | _this->type == PPP_AUTH_CHAP_MS_V2); |
| 436 | |
| 437 | ppp_output(_this->ppp, PPP_PROTO_CHAP0xC223, (authok)? 3 : 4, _this->challid, |
| 438 | pktp, lpktp); |
| 439 | |
| 440 | realm_name = npppd_ppp_get_realm_name(_this->ppp->pppd, _this->ppp); |
| 441 | if (!authok) { |
| 442 | chap_log(_this, LOG_ALERT1, |
| 443 | "logtype=Failure username=\"%s\" realm=%s", _this->name, |
| 444 | realm_name); |
| 445 | chap_stop(_this); |
| 446 | /* Stop the PPP if the authentication is failed. */ |
| 447 | ppp_set_disconnect_cause(_this->ppp, |
| 448 | PPP_DISCON_AUTH_FAILED, PPP_PROTO_CHAP0xC223, 1 /* peer */, NULL((void *)0)); |
| 449 | ppp_stop(_this->ppp, "Authentication Required"); |
| 450 | } else { |
| 451 | strlcpy(_this->ppp->username, _this->name, |
| 452 | sizeof(_this->ppp->username)); |
| 453 | chap_log(_this, LOG_INFO6, |
| 454 | "logtype=Success username=\"%s\" " |
| 455 | "realm=%s", _this->name, realm_name); |
| 456 | chap_stop(_this); |
| 457 | /* We change our state to prepare to resend requests. */ |
| 458 | _this->state = CHAP_STATE_SENT_RESPONSE4; |
| 459 | ppp_auth_ok(_this->ppp); |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | /** Generate a challenge */ |
| 464 | static void |
| 465 | chap_create_challenge(chap *_this) |
| 466 | { |
| 467 | CHAP_ASSERT(_this->ppp->peer_auth == PPP_AUTH_CHAP_MS_V2 || |
| 468 | _this->ppp->peer_auth == PPP_AUTH_CHAP_MD5); |
| 469 | |
| 470 | _this->lchall = 16; |
| 471 | arc4random_buf(_this->chall, _this->lchall); |
| 472 | } |
| 473 | |
| 474 | /*********************************************************************** |
| 475 | * Proxy Authentication |
| 476 | ***********************************************************************/ |
| 477 | int |
| 478 | chap_proxy_authen_prepare(chap *_this, dialin_proxy_info *dpi) |
| 479 | { |
| 480 | |
| 481 | CHAP_ASSERT(dpi->auth_type == PPP_AUTH_CHAP_MD5); |
| 482 | CHAP_ASSERT(_this->state == CHAP_STATE_INITIAL); |
| 483 | |
| 484 | _this->pktid = dpi->auth_id; |
| 485 | |
| 486 | #ifdef USE_NPPPD_MPPE1 |
| 487 | if (MPPE_IS_REQUIRED(_this->ppp)(((_this->ppp)->mppe.enabled != 0) && ((_this-> ppp)->mppe.required != 0)) && |
| 488 | _this->type != PPP_AUTH_CHAP_MS_V20x81) { |
| 489 | chap_log(_this, LOG_ALERT1, |
| 490 | "mppe is required but try to start chap " |
| 491 | "type=0x%02x", dpi->auth_type); |
| 492 | return -1; |
| 493 | } |
| 494 | #endif |
| 495 | /* authentication */ |
| 496 | if (strlen(dpi->username) >= sizeof(_this->name)) { |
| 497 | chap_log(_this, LOG_NOTICE5, |
| 498 | "\"Proxy Authen Name\" is too long."); |
| 499 | return -1; |
| 500 | } |
| 501 | if (dpi->lauth_chall >= sizeof(_this->chall)) { |
| 502 | chap_log(_this, LOG_NOTICE5, |
| 503 | "\"Proxy Authen Challenge\" is too long."); |
| 504 | return -1; |
| 505 | } |
| 506 | |
| 507 | /* copy the authentication properties */ |
| 508 | CHAP_ASSERT(_this->ppp->proxy_authen_resp == NULL); |
| 509 | if ((_this->ppp->proxy_authen_resp = malloc(dpi->lauth_resp)) == |
| 510 | NULL((void *)0)) { |
| 511 | chap_log(_this, LOG_ERR3, "malloc() failed in %s(): %m", |
| 512 | __func__); |
| 513 | return -1; |
| 514 | } |
| 515 | memcpy(_this->ppp->proxy_authen_resp, dpi->auth_resp, |
| 516 | dpi->lauth_resp); |
| 517 | _this->ppp->lproxy_authen_resp = dpi->lauth_resp; |
| 518 | |
| 519 | _this->challid = dpi->auth_id; |
| 520 | strlcpy(_this->name, dpi->username, sizeof(_this->name)); |
| 521 | |
| 522 | memcpy(_this->chall, dpi->auth_chall, dpi->lauth_chall); |
| 523 | _this->lchall = dpi->lauth_chall; |
| 524 | |
| 525 | _this->state = CHAP_STATE_PROXY_AUTHENTICATION6; |
| 526 | |
| 527 | return 0; |
| 528 | } |
| 529 | |
| 530 | /************************************************************************ |
| 531 | * Functions for MD5-CHAP(RFC1994) |
| 532 | ************************************************************************/ |
| 533 | static void |
| 534 | md5chap_authenticate(chap *_this, int id, char *username, u_char *challenge, |
| 535 | int lchallenge, u_char *response) |
| 536 | { |
| 537 | MD5_CTX md5ctx; |
| 538 | int rval, passlen; |
| 539 | u_char digest[16]; |
| 540 | char *password, buf[MAX_PASSWORD_LENGTH256 + 1]; |
| 541 | |
| 542 | buf[0] = id; |
| 543 | passlen = sizeof(buf) - 1; |
| 544 | password = &buf[1]; |
| 545 | |
| 546 | rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, |
| 547 | password, &passlen); |
| 548 | |
| 549 | if (rval != 0) { |
| 550 | switch (rval) { |
| 551 | case 1: |
| 552 | chap_log(_this, LOG_INFO6, |
| 553 | "username=\"%s\" user unknown", username); |
| 554 | break; |
| 555 | default: |
| 556 | chap_log(_this, LOG_ERR3, |
| 557 | "username=\"%s\" generic error", username); |
| 558 | break; |
| 559 | } |
| 560 | goto auth_failed; |
| 561 | } |
| 562 | passlen = strlen(password); |
| 563 | MD5Init(&md5ctx); |
| 564 | MD5Update(&md5ctx, buf, passlen + 1); |
| 565 | MD5Update(&md5ctx, challenge, lchallenge); |
| 566 | MD5Final(digest, &md5ctx); |
| 567 | |
| 568 | if (memcmp(response, digest, 16) == 0) { |
| 569 | chap_response(_this, 1, "OK", 2); |
| 570 | return; |
| 571 | } |
| 572 | /* FALLTHROUGH. The password are not matched */ |
| 573 | auth_failed: |
| 574 | /* No extra information, just "FAILED" */ |
| 575 | chap_send_error(_this, "FAILED"); |
| 576 | |
| 577 | return; |
| 578 | } |
| 579 | |
| 580 | static void |
| 581 | chap_send_error(chap *_this, const char *msg) |
| 582 | { |
| 583 | u_char *pkt, *challenge; |
| 584 | int lpkt; |
| 585 | |
| 586 | challenge = _this->chall; |
| 587 | |
| 588 | pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP0xC223) + HEADERLEN4; |
| 589 | lpkt = _this->ppp->mru - HEADERLEN4; |
| 590 | |
| 591 | strlcpy(pkt, msg, lpkt); |
| 592 | lpkt = strlen(msg); |
| 593 | |
| 594 | chap_response(_this, 0, pkt, lpkt); |
| 595 | } |
| 596 | |
| 597 | /************************************************************************ |
| 598 | * Functions for MS-CHAP-V2(RFC 2759) |
| 599 | ************************************************************************/ |
| 600 | static void |
| 601 | mschapv2_send_error(chap *_this, int error, int can_retry) |
| 602 | { |
| 603 | u_char *pkt, *challenge; |
| 604 | int lpkt; |
| 605 | |
| 606 | challenge = _this->chall; |
| 607 | |
| 608 | pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP0xC223) + HEADERLEN4; |
| 609 | lpkt = _this->ppp->mru - HEADERLEN4; |
| 610 | |
| 611 | /* |
| 612 | * We don't use "M=<msg>" |
| 613 | * - pppd on Mac OS 10.4 hungs up if it received a failure packet |
| 614 | * with "M=<msg>". |
| 615 | * - RRAS on windows server 2003 never uses "M=". |
| 616 | */ |
| 617 | snprintf(pkt, lpkt, "E=%d R=%d C=%02x%02x%02x%02x%02x%02x%02x%02x" |
| 618 | "%02x%02x%02x%02x%02x%02x%02x%02x V=3", error, can_retry, |
| 619 | challenge[0], challenge[1], challenge[2], challenge[3], |
| 620 | challenge[4], challenge[5], challenge[6], challenge[7], |
| 621 | challenge[8], challenge[9], challenge[10], challenge[11], |
| 622 | challenge[12], challenge[13], challenge[14], challenge[15] |
| 623 | ); |
| 624 | lpkt = strlen(pkt); |
| 625 | |
| 626 | chap_response(_this, 0, pkt, lpkt); |
| 627 | } |
| 628 | |
| 629 | static void |
| 630 | mschapv2_authenticate(chap *_this, int id, char *username, u_char *challenge, |
| 631 | int lchallenge, u_char *response) |
| 632 | { |
| 633 | int i, rval, passlen, lpkt; |
| 634 | u_char *pkt; |
| 635 | char password[MAX_PASSWORD_LENGTH256 * 2], ntresponse[24]; |
| 636 | #ifdef USE_NPPPD_MPPE1 |
| 637 | char pwdhash[16], pwdhashhash[16]; |
| 638 | #endif |
| 639 | |
| 640 | CHAP_DBG((_this, LOG_DEBUG, "%s()", __func__)); |
| 641 | pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP0xC223) + HEADERLEN4; |
| 642 | lpkt = _this->ppp->mru - HEADERLEN4; |
| 643 | |
| 644 | passlen = sizeof(password) / 2; |
| 645 | rval = npppd_get_user_password(_this->ppp->pppd, _this->ppp, username, |
| 646 | password, &passlen); |
| 647 | |
| 648 | if (rval != 0) { |
| 649 | switch (rval) { |
| 650 | case 1: |
| 651 | chap_log(_this, LOG_INFO6, |
| 652 | "username=\"%s\" user unknown", username); |
| 653 | break; |
| 654 | default: |
| 655 | chap_log(_this, LOG_ERR3, |
| 656 | "username=\"%s\" generic error", username); |
| 657 | break; |
| 658 | } |
| 659 | goto auth_failed; |
| 660 | } |
| 661 | |
| 662 | /* Convert the string charset from ASCII to UTF16-LE */ |
| 663 | passlen = strlen(password); |
| 664 | for (i = passlen - 1; i >= 0; i--) { |
| 665 | password[i*2] = password[i]; |
| 666 | password[i*2+1] = 0; |
| 667 | } |
| 668 | |
| 669 | mschap_nt_response(challenge, response, username, strlen(username), |
| 670 | password, passlen * 2, ntresponse); |
| 671 | |
| 672 | if (memcmp(ntresponse, response + 24, 24) != 0) { |
| 673 | chap_log(_this, LOG_INFO6, |
| 674 | "username=\"%s\" password mismatch.", username); |
| 675 | goto auth_failed; |
| 676 | } |
| 677 | |
| 678 | /* |
| 679 | * Authentication succeed |
| 680 | */ |
| 681 | CHAP_DBG((_this, LOG_DEBUG, "%s() OK", __func__)); |
| 682 | |
| 683 | mschap_auth_response(password, passlen * 2, ntresponse, |
| 684 | challenge, response, username, strlen(username), pkt); |
| 685 | lpkt = 42; |
| 686 | #ifdef USE_NPPPD_MPPE1 |
| 687 | if (_this->ppp->mppe.enabled != 0) { |
| 688 | mschap_ntpassword_hash(password, passlen * 2, pwdhash); |
| 689 | mschap_ntpassword_hash(pwdhash, sizeof(pwdhash), pwdhashhash); |
| 690 | |
| 691 | mschap_masterkey(pwdhashhash, ntresponse, |
| 692 | _this->ppp->mppe.master_key); |
| 693 | mschap_asymetric_startkey(_this->ppp->mppe.master_key, |
| 694 | _this->ppp->mppe.recv.master_key, MPPE_KEYLEN16, 0, 1); |
| 695 | mschap_asymetric_startkey(_this->ppp->mppe.master_key, |
| 696 | _this->ppp->mppe.send.master_key, MPPE_KEYLEN16, 1, 1); |
| 697 | } |
| 698 | #endif |
| 699 | chap_response(_this, 1, pkt, lpkt); |
| 700 | |
| 701 | return; |
| 702 | auth_failed: |
| 703 | /* No extra information */ |
| 704 | mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE691, 0); |
| 705 | |
| 706 | return; |
| 707 | } |
| 708 | |
| 709 | #ifdef USE_NPPPD_RADIUS1 |
| 710 | /************************************************************************ |
| 711 | * Functions for RADIUS |
| 712 | * RFC 2058: RADIUS |
| 713 | * RFC 2548: Microsoft Vendor-specific RADIUS Attributes |
| 714 | ************************************************************************/ |
| 715 | static void |
| 716 | chap_radius_authenticate(chap *_this, int id, char *username, |
| 717 | u_char *challenge, int lchallenge, u_char *response) |
| 718 | { |
| 719 | void *radctx; |
| 720 | RADIUS_PACKET *radpkt; |
| 721 | radius_req_setting *rad_setting; |
| 722 | int lpkt; |
| 723 | u_char *pkt; |
| 724 | char buf0[MAX_USERNAME_LENGTH256]; |
| 725 | |
| 726 | radpkt = NULL((void *)0); |
| 727 | radctx = NULL((void *)0); |
| 728 | |
| 729 | if ((rad_setting = npppd_get_radius_auth_setting(_this->ppp->pppd, |
| 730 | _this->ppp)) == NULL((void *)0)) { |
| 731 | goto fail; /* no radius server */ |
| 732 | } |
| 733 | pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP0xC223) + HEADERLEN4; |
| 734 | lpkt = _this->ppp->mru - HEADERLEN4; |
Value stored to 'lpkt' is never read | |
| 735 | |
| 736 | if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST1)) |
| 737 | == NULL((void *)0)) |
| 738 | goto fail; |
| 739 | if (radius_prepare(rad_setting, _this, &radctx, chap_radius_response) |
| 740 | != 0) { |
| 741 | radius_delete_packet(radpkt); |
| 742 | goto fail; |
| 743 | } |
| 744 | |
| 745 | if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) |
| 746 | != 0) |
| 747 | goto fail; |
| 748 | |
| 749 | if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME1, |
| 750 | npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, |
| 751 | username, buf0)) != 0) |
| 752 | goto fail; |
| 753 | |
| 754 | switch (_this->type) { |
| 755 | case PPP_AUTH_CHAP_MD50x05: |
| 756 | { |
| 757 | u_char md5response[17]; |
| 758 | |
| 759 | md5response[0] = _this->challid; |
| 760 | memcpy(&md5response[1], response, 16); |
| 761 | if (radius_put_raw_attr(radpkt, |
| 762 | RADIUS_TYPE_CHAP_PASSWORD3, md5response, 17) != 0) |
| 763 | goto fail; |
| 764 | if (radius_put_raw_attr(radpkt, |
| 765 | RADIUS_TYPE_CHAP_CHALLENGE60, challenge, lchallenge) != 0) |
| 766 | goto fail; |
| 767 | break; |
| 768 | } |
| 769 | case PPP_AUTH_CHAP_MS_V20x81: |
| 770 | { |
| 771 | struct RADIUS_MS_CHAP2_RESPONSE msresponse; |
| 772 | |
| 773 | /* Preparing RADIUS_MS_CHAP2_RESPONSE */ |
| 774 | memset(&msresponse, 0, sizeof(msresponse)); |
| 775 | msresponse.ident = id; |
| 776 | msresponse.flags = response[48]; |
| 777 | memcpy(&msresponse.peer_challenge, response, 16); |
| 778 | memcpy(&msresponse.response, response + 24, 24); |
| 779 | |
| 780 | if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT311, |
| 781 | RADIUS_VTYPE_MS_CHAP_CHALLENGE11, challenge, 16) != 0) |
| 782 | goto fail; |
| 783 | if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT311, |
| 784 | RADIUS_VTYPE_MS_CHAP2_RESPONSE25, &msresponse, |
| 785 | sizeof(msresponse)) != 0) |
| 786 | goto fail; |
| 787 | break; |
| 788 | } |
| 789 | |
| 790 | } |
| 791 | radius_get_authenticator(radpkt, _this->authenticator); |
| 792 | |
| 793 | /* Cancel previous request */ |
| 794 | if (_this->radctx != NULL((void *)0)) |
| 795 | radius_cancel_request(_this->radctx); |
| 796 | |
| 797 | /* Send a request */ |
| 798 | _this->radctx = radctx; |
| 799 | radius_request(radctx, radpkt); |
| 800 | |
| 801 | return; |
| 802 | fail: |
| 803 | switch (_this->type) { |
| 804 | case PPP_AUTH_CHAP_MD50x05: |
| 805 | /* No extra information, just "FAILED" */ |
| 806 | chap_send_error(_this, "FAILED"); |
| 807 | break; |
| 808 | case PPP_AUTH_CHAP_MS_V20x81: |
| 809 | /* No extra information */ |
| 810 | mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE691, 0); |
| 811 | break; |
| 812 | } |
| 813 | if (radctx != NULL((void *)0)) |
| 814 | radius_cancel_request(radctx); |
| 815 | } |
| 816 | |
| 817 | static void |
| 818 | chap_radius_response(void *context, RADIUS_PACKET *pkt, int flags, |
| 819 | RADIUS_REQUEST_CTX reqctx) |
| 820 | { |
| 821 | int code, lrespkt; |
| 822 | const char *secret, *reason = ""; |
| 823 | chap *_this; |
| 824 | u_char *respkt, *respkt0; |
| 825 | int errorCode; |
| 826 | RADIUS_REQUEST_CTX radctx; |
| 827 | |
| 828 | CHAP_ASSERT(context != NULL); |
| 829 | |
| 830 | reason = ""; |
| 831 | errorCode = ERROR_AUTH_SERVER_TIMEOUT930; |
| 832 | _this = context; |
| 833 | secret = radius_get_server_secret(_this->radctx); |
| 834 | radctx = _this->radctx; |
| 835 | _this->radctx = NULL((void *)0); /* IMPORTANT */ |
| 836 | |
| 837 | respkt = respkt0 = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP0xC223) |
| 838 | + HEADERLEN4; |
| 839 | lrespkt = _this->ppp->mru - HEADERLEN4; |
| 840 | if (pkt == NULL((void *)0)) { |
| 841 | if (flags & RADIUS_REQUEST_TIMEOUT0x0002) |
| 842 | reason = "timeout"; |
| 843 | else if (flags & RADIUS_REQUEST_ERROR0x0001) |
| 844 | reason = strerror(errno(*__errno())); |
| 845 | else |
| 846 | reason = "error"; |
| 847 | goto auth_failed; |
| 848 | } |
| 849 | |
| 850 | code = radius_get_code(pkt); |
| 851 | if (code == RADIUS_CODE_ACCESS_REJECT3) { |
| 852 | reason="reject"; |
| 853 | errorCode = ERROR_AUTHENTICATION_FAILURE691; |
| 854 | /* Windows peer will reset the password by this error code */ |
| 855 | goto auth_failed; |
| 856 | } else if (code != RADIUS_CODE_ACCESS_ACCEPT2) { |
| 857 | reason="error"; |
| 858 | goto auth_failed; |
| 859 | } |
| 860 | if ((flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK0x0010) == 0 && |
| 861 | (flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK0x0020) == 0) { |
| 862 | reason="bad_authenticator"; |
| 863 | goto auth_failed; |
| 864 | } |
| 865 | /* |
| 866 | * Authentication OK |
| 867 | */ |
| 868 | switch (_this->type) { |
| 869 | case PPP_AUTH_CHAP_MD50x05: |
| 870 | chap_response(_this, 1, "OK", 2); |
| 871 | break; |
| 872 | case PPP_AUTH_CHAP_MS_V20x81: |
| 873 | { |
| 874 | struct RADIUS_MS_CHAP2_SUCCESS success; |
| 875 | #ifdef USE_NPPPD_MPPE1 |
| 876 | struct RADIUS_MPPE_KEY sendkey, recvkey; |
| 877 | #endif |
| 878 | size_t len; |
| 879 | |
| 880 | len = sizeof(success); |
| 881 | if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT311, |
| 882 | RADIUS_VTYPE_MS_CHAP2_SUCCESS26, &success, &len) != 0) { |
| 883 | chap_log(_this, LOG_ERR3, "no ms_chap2_success"); |
| 884 | goto auth_failed; |
| 885 | } |
| 886 | #ifdef USE_NPPPD_MPPE1 |
| 887 | if (_this->ppp->mppe.enabled != 0) { |
| 888 | len = sizeof(sendkey); |
| 889 | if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT311, |
| 890 | RADIUS_VTYPE_MPPE_SEND_KEY16, &sendkey, &len) != 0) { |
| 891 | chap_log(_this, LOG_ERR3, "no mppe_send_key"); |
| 892 | goto auth_failed; |
| 893 | } |
| 894 | len = sizeof(recvkey); |
| 895 | if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT311, |
| 896 | RADIUS_VTYPE_MPPE_RECV_KEY17, &recvkey, &len) != 0) { |
| 897 | chap_log(_this, LOG_ERR3, "no mppe_recv_key"); |
| 898 | goto auth_failed; |
| 899 | } |
| 900 | |
| 901 | mschap_radiuskey(_this->ppp->mppe.send.master_key, |
| 902 | sendkey.salt, _this->authenticator, secret); |
| 903 | |
| 904 | mschap_radiuskey(_this->ppp->mppe.recv.master_key, |
| 905 | recvkey.salt, _this->authenticator, secret); |
| 906 | } |
| 907 | #endif |
| 908 | chap_response(_this, 1, success.str, sizeof(success.str)); |
| 909 | break; |
| 910 | } |
| 911 | } |
| 912 | ppp_process_radius_framed_ip(_this->ppp, pkt); |
| 913 | |
| 914 | return; |
| 915 | auth_failed: |
| 916 | chap_log(_this, LOG_WARNING4, "Radius authentication request failed: %s", |
| 917 | reason); |
| 918 | /* log reply messages from radius server */ |
| 919 | if (pkt != NULL((void *)0)) { |
| 920 | char radmsg[255], vissed[1024]; |
| 921 | size_t rmlen = 0; |
| 922 | if ((radius_get_raw_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE18, |
| 923 | radmsg, &rmlen)) == 0) { |
| 924 | if (rmlen != 0) { |
| 925 | strvisx(vissed, radmsg, rmlen, VIS_WHITE(0x04 | 0x08 | 0x10)); |
| 926 | chap_log(_this, LOG_WARNING4, |
| 927 | "Radius reply message: %s", vissed); |
| 928 | } |
| 929 | } |
| 930 | } |
| 931 | |
| 932 | /* No extra information */ |
| 933 | chap_failure(_this, "FAILED", errorCode); |
| 934 | } |
| 935 | |
| 936 | #endif |
| 937 | |
| 938 | /************************************************************************ |
| 939 | * Miscellaneous functions |
| 940 | ************************************************************************/ |
| 941 | static char * |
| 942 | strip_nt_domain(char *username) |
| 943 | { |
| 944 | char *lastbackslash; |
| 945 | |
| 946 | if ((lastbackslash = strrchr(username, '\\')) != NULL((void *)0)) |
| 947 | return lastbackslash + 1; |
| 948 | |
| 949 | return username; |
| 950 | } |
| 951 | |
| 952 | static void |
| 953 | chap_log(chap *_this, uint32_t prio, const char *fmt, ...) |
| 954 | { |
| 955 | const char *protostr; |
| 956 | char logbuf[BUFSIZ1024]; |
| 957 | va_list ap; |
| 958 | |
| 959 | CHAP_ASSERT(_this != NULL); |
| 960 | CHAP_ASSERT(_this->ppp != NULL); |
| 961 | |
| 962 | switch (_this->type) { |
| 963 | case PPP_AUTH_CHAP_MD50x05: |
| 964 | protostr = "chap"; |
| 965 | break; |
| 966 | case PPP_AUTH_CHAP_MS_V20x81: |
| 967 | protostr = "mschap_v2"; |
| 968 | break; |
| 969 | default: |
| 970 | protostr = "unknown"; |
| 971 | break; |
| 972 | } |
| 973 | |
| 974 | va_start(ap, fmt)__builtin_va_start(ap, fmt); |
| 975 | snprintf(logbuf, sizeof(logbuf), "ppp id=%u layer=chap proto=%s %s", |
| 976 | _this->ppp->id, protostr, fmt); |
| 977 | vlog_printf(prio, logbuf, ap); |
| 978 | va_end(ap)__builtin_va_end(ap); |
| 979 | } |