/* This file is in the public domain. */ #include #include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "algs.h" #include "msgs.h" #include "util.h" #include "errf.h" #include "panic.h" #include "config.h" #include "nested.h" #include "pkt-util.h" typedef struct uacc_state UACC_STATE; struct uacc_state { int stage; #define UAS_RQSERV 1 #define UAS_RUNNING 2 #define UAS_DONE 3 PKTQ oq; CUASTATE uastate; } ; static void send_service_request(LAYER *l) { unsigned char *opp; l->b->opkt[0] = SSH_MSG_SERVICE_REQUEST; opp = put_string(&l->b->opkt[1],"ssh-userauth",-1); l->b->oplen = opp - &l->b->opkt[0]; below_opkt(l); } static unsigned char *pkthead(CUASTATE *s) { unsigned char *opp; const char *u; u = config_str("user"); if (! u) { fprintf(errf,"%s: no $USER and no remote username specified\n",__progname); exit(1); } opp = &s->layer->b->opkt[0]; *opp++ = SSH_MSG_USERAUTH_REQUEST; opp = put_string(opp,u,-1); opp = put_string(opp,"ssh-connection",-1); opp = put_string(opp,(*at_ua.name)(s->curalg),-1); return(opp); } static void start_method(LAYER *l, CUASTATE *s, UAALG *m) { if (VERB(AUTH)) verb(AUTH,"Starting auth algorithm `%s'\n",m->name); s->curalg = m; s->algpriv = 0; l->b->oplen = (*s->curalg->c.start)(s) - &l->b->opkt[0]; below_opkt(l); } static void handle_banner(BPP *b) { STR msg; STR lang; parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&msg), PP_STRING(&lang), PP_ENDSHERE ); printf("Banner"); if (lang.len) printf(" (language %.*s)",lang.len,lang.data); printf("\n%.*s\n",msg.len,msg.data); } static void cantdo(CUASTATE *s, void *alg) { ALE *l; ALE **lp; lp = &s->cando; while ((l = *lp)) { if (l->alg == alg) { *lp = l->link; free(l); } else { lp = &l->link; } } } static void next_alg(CUASTATE *s, ALE *contin) { ALE *l; (*s->curalg->c.done)(s); cantdo(s,s->curalg); do <"gotalg"> { for (l=s->cando;l;l=l->link) { ALE *l2; for (l2=contin;l2;l2=l2->link) { if (l2->alg == l->alg) break <"gotalg">; } } fprintf(errf,"%s: authentication failed (no more algs)\n",__progname); exit(1); } while (0); start_method(s->layer,s,l->alg); } static void ale_list_free(ALE *l) { ALE *e; while (l) { e = l->link; free(l); l = e; } } static void userauth_failure(CUASTATE *s) { BPP *b; STR methods; int partial; ALE *contin; int curlisted; ALE *l; int rv; b = s->layer->b; parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&methods), PP_BOOL(&partial), PP_ENDSHERE ); /* Four cases: (1) partial success, current algorithm listed in methods current alg running -> continue current alg (2) partial success, current algorithm not listed in methods current alg succeeded, more auth needed -> end current alg -> pick next alg -> start it (3) no partial success, current algorithm listed in methods not sure what this can mean - maybe, eg, public key failed, but another public key might succeed? -> retry current alg (4) no partial success, current algorithm not listed in methods current alg failed, try another -> end current alg -> pick next alg -> start it */ { ALE **lp; static int showed = 0; NESTED int savemethod(const void *name, int len) { ALE *l; void *a; a = (*at_ua.find_rostr)(blk_to_rostr(name,len)); if ((VERB(OFFER) && !showed) || VERB(AUTH)) { pverb(VERBOSE_OFFER|VERBOSE_AUTH,"\t%c %.*s\n",a?alglist_present(&algs_ua,a)?' ':'*':'%',len,(const char *)name); } if (a) { l = malloc(sizeof(ALE)); l->alg = a; *lp = l; lp = &l->link; } return(0); } if ((VERB(OFFER) && !showed) || VERB(AUTH)) { pverb(VERBOSE_OFFER|VERBOSE_AUTH,"Authentication algorithms offered to us (%%=unrecognized, *=disabled):\n"); } lp = &contin; comma_list(methods.data,methods.len,&savemethod); *lp = 0; showed = 1; } do <"ret"> { curlisted = 0; for (l=contin;l;l=l->link) { if (l->alg == s->curalg) { curlisted = 1; break; } } if (VERB(AUTH)) { verb(AUTH,"%s, %s\n", partial ? "Partial success" : "No partial success", curlisted ? "current alg listed" : "current alg not listed" ); } if (curlisted) { rv = partial ? (*s->curalg->c.partial)(s) : (*s->curalg->c.retry)(s); switch (rv) { case UA_READ: if (VERB(AUTH)) verb(AUTH,"UA_READ: await another packet\n"); break <"ret">; break; case UA_SEND: if (VERB(AUTH)) verb(AUTH,"UA_SEND: send and await another packet\n"); below_opkt(s->layer); break <"ret">; break; case UA_NEXT: if (VERB(AUTH)) verb(AUTH,"UA_NEXT: give up, try next alg\n"); break; default: panic("bad status"); break; } } /* Case 2 or 4, or a UA_NEXT from a (*partial) call */ /* End current alg, pick new one, start it */ next_alg(s,contin); } while (0); ale_list_free(contin); } static void *uac_c_init(LAYER *l) { UACC_STATE *s; ALGLIST list; s = malloc(sizeof(UACC_STATE)); s->stage = UAS_RQSERV; send_service_request(l); pktq_init(&s->oq); s->uastate.layer = l; s->uastate.curalg = 0; s->uastate.algpriv = 0; s->uastate.pkthead = &pkthead; alglist_init(&list,&at_ua); alglist_map(&algs_ua,({ NESTED int foo(void *alg) { if ((*((UAALG *)alg)->c.canuse)(l->b)) { alglist_append1(&list,alg); } return(0); } &foo; })); s->uastate.cando = list.head; return(s); } static void uac_c_r(LAYER *l, void *arg) { UACC_STATE *s; BPP *b; int rv; s = arg; b = l->b; switch (s->stage) { case UAS_RQSERV: switch (b->ipkt[0]) { case SSH_MSG_SERVICE_ACCEPT: start_method(l,&s->uastate,(*at_ua.find_c)("none")); s->stage = UAS_RUNNING; break; case SSH_MSG_USERAUTH_BANNER: handle_banner(b); break; default: badmsg("RQSERV","type %d",b->ipkt[0]); break; } break; case UAS_RUNNING: switch (b->ipkt[0]) { case SSH_MSG_USERAUTH_SUCCESS: if (VERB(AUTH)) verb(AUTH,"Auth succeeded\n"); (*s->uastate.curalg->c.done)(&s->uastate); s->stage = UAS_DONE; while (pktq_get(&s->oq,&b->opkt[0],&b->oplen,sizeof(b->opkt))) { below_opkt(l); } break; case SSH_MSG_USERAUTH_FAILURE: if (VERB(AUTH)) verb(AUTH,"Auth failed\n"); userauth_failure(&s->uastate); break; case SSH_MSG_USERAUTH_BANNER: handle_banner(b); break; case 60 ... 79: rv = (*s->uastate.curalg->c.run)(&s->uastate); switch (rv) { case UA_READ: break; case UA_SEND: below_opkt(s->uastate.layer); break; case UA_NEXT: /* We don't have a contin list at this point, which is annoying. Get one with "none". */ (*s->uastate.curalg->c.done)(&s->uastate); cantdo(&s->uastate,s->uastate.curalg); start_method(l,&s->uastate,(*at_ua.find_c)("none")); break; default: panic("bad status"); break; } break; default: badmsg("RUNNING","type %d",b->ipkt[0]); break; } break; case UAS_DONE: above_ipkt(l); break; } } static void uac_c_w(LAYER *l, void *arg) { UACC_STATE *s; s = arg; switch (s->stage) { default: pktq_put(&s->oq,&l->b->opkt[0],l->b->oplen); break; case UAS_DONE: below_opkt(l); break; } } LAYERDESC layer_userauth_conn_c = { &uac_c_init, &uac_c_r, &uac_c_w };