#include #include #include #include #include #include #include #include #define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ (((unsigned char*) (s))[1])) #define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \ ((unsigned char*)(s))[1] = (i) & 0xff;} #ifndef D /*#define D(str) fprintf(stderr, (str)) */ #define D(str) #endif static int read_fill(int fd, unsigned char *buf, int len) { int i, got = 0; do { if ((i = read(fd, buf+got, len-got)) <= 0) { if (i == 0) return got; if (errno != EINTR) return got; i = 0; } got += i; } while (got < len); return (len); } static int write_fill(int fd, char *buf, int len) { int i, done = 0; do { if ((i = write(fd, buf+done, len-done)) < 0) { if (errno != EINTR) return (i); i = 0; } done += i; } while (done < len); return (len); } #if 0 /* * These functions are for binary prompt manipulation. * The manner in which a binary prompt is processed is application * specific, so these function pointers are provided and can be * initialized by the application prior to the conversation function * being used. */ static void pam_misc_conv_delete_binary(void *appdata, pamc_bp_t *delete_me) { PAM_BP_RENEW(delete_me, 0, 0); } int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p) = NULL; void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p) = pam_misc_conv_delete_binary; #endif /* * This conversation function is supposed to be a generic PAM one. * Unfortunately, it is _not_ completely compatible with the Solaris PAM * codebase. * * Namely, for msgm's that contain multiple prompts, this function * interprets "const struct pam_message **msgm" as equivalent to * "const struct pam_message *msgm[]". The Solaris module * implementation interprets the **msgm object as a pointer to a * pointer to an array of "struct pam_message" objects (that is, a * confusing amount of pointer indirection). */ int misc_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) { int count=0; struct pam_response *reply; if (num_msg <= 0) return PAM_CONV_ERR; D(("allocating empty response structure array.")); reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) { D(("no memory for responses")); return PAM_CONV_ERR; } D(("entering conversation function.")); for (count=0; count < num_msg; ++count) { char *string=NULL; switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: string = (char*)appdata_ptr; break; case PAM_PROMPT_ECHO_ON: string = (char*)appdata_ptr; break; case PAM_ERROR_MSG: if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) { goto failed_conversation; } break; case PAM_TEXT_INFO: if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) { goto failed_conversation; } break; #if 0 case PAM_BINARY_PROMPT: { pamc_bp_t binary_prompt = NULL; if (!msgm[count]->msg || !pam_binary_handler_fn) { goto failed_conversation; } PAM_BP_RENEW(&binary_prompt, PAM_BP_RCONTROL(msgm[count]->msg), PAM_BP_LENGTH(msgm[count]->msg)); PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg), PAM_BP_RDATA(msgm[count]->msg)); if (pam_binary_handler_fn(appdata_ptr, &binary_prompt) != PAM_SUCCESS || (binary_prompt == NULL)) { goto failed_conversation; } string = (char *) binary_prompt; binary_prompt = NULL; break; } #endif default: fprintf(stderr, "erroneous conversation (%d)\n" ,msgm[count]->msg_style); goto failed_conversation; } if (string) { /* must add to reply array */ /* add string to list of responses */ reply[count].resp_retcode = 0; reply[count].resp = string; string = NULL; } } *response = reply; reply = NULL; return PAM_SUCCESS; failed_conversation: D(("the conversation failed")); if (reply) { for (count=0; countmsg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: #if 0 _pam_overwrite(reply[count].resp); #endif free(reply[count].resp); break; #if 0 case PAM_BINARY_PROMPT: pam_binary_handler_free(appdata_ptr, (pamc_bp_t *) &reply[count].resp); break; #endif case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* should not actually be able to get here... */ free(reply[count].resp); } reply[count].resp = NULL; } /* forget reply too */ free(reply); reply = NULL; } return PAM_CONV_ERR; } static struct pam_conv conv = { misc_conv, NULL }; static void werr(pam_handle_t *pamh, int sid, int ecode, char *phase) { char buf[BUFSIZ]; int len; sprintf(&buf[2], "pam %d no %s %s", sid, phase, pam_strerror(pamh, ecode)); len = strlen(&buf[2]); put_int16(len, &buf[0]); if (write_fill(1, buf, len+2) != len+2) exit(1); } static void wok(int sid) { char buf[BUFSIZ]; int len; sprintf(&buf[2], "pam %d yes", sid); len = strlen(&buf[2]); put_int16(len, &buf[0]); if (write_fill(1, buf, len+2) != len+2) exit(1); } static void wstart() { char buf[5]; sprintf(&buf[2], "ok"); put_int16(2, &buf[0]); if (write_fill(1, buf, 4) != 4) { exit(1); } } struct session { pam_handle_t *pamh; int sid; int session_mode; struct session *next; }; static struct session *sessions = NULL; static struct session *del_session(struct session **sp, int sid) { struct session *tmp; if (*sp == NULL) return NULL; if ((*sp)->sid == sid) { tmp = *sp; *sp = tmp->next; return tmp; } tmp = (*sp)->next; while (tmp != NULL) { if (tmp->sid == sid) { (*sp)->next = tmp->next; return tmp; } sp = &((*sp)->next); tmp = tmp->next; } return NULL; } static void do_auth(char *service, char*user, char*pwd, char* mode, int sid) { pam_handle_t *pamh=NULL; int retval; struct session *sessp; conv.appdata_ptr = (void*)strdup(pwd); retval = pam_start(service, user, &conv, &pamh); if (retval != PAM_SUCCESS) { werr(pamh, sid, retval, "start"); return; } pam_set_item(pamh, PAM_RUSER, user); retval = pam_authenticate(pamh, 0); if (retval != PAM_SUCCESS) { werr(pamh, sid, retval, "auth"); return; } if (mode[0] == 'A') { retval = pam_acct_mgmt(pamh, 0); if (retval != PAM_SUCCESS) { werr(pamh, sid, retval, "accounting"); return; } /*fprintf(stderr, "did ok acct \n\r");*/ } if (mode[1] == 'S') { retval = pam_open_session(pamh, 0); if (retval != PAM_SUCCESS) { werr(pamh, sid, retval, "session"); return; } /*fprintf(stderr, "did ok open sess \n\r"); */ } if ((sessp = malloc(sizeof(struct session))) == NULL) { werr(pamh, sid, -1, "malloc"); return; } if (mode[1] == 'S') sessp->session_mode = 1; else sessp->session_mode = 0; sessp->sid = sid; sessp->pamh = pamh; sessp->next = sessions; sessions = sessp; wok(sid); } int main(int argc, char *argv[]) { pam_handle_t *pamh=NULL; unsigned char lb[2]; unsigned char buf[BUFSIZ]; char *user; char *pwd; char *mode; int sid; int rval; struct session *sessp; // test clause if (argc == 4 ) { /* ./epam authmodule user passwd */ printf("testing service=%s u=%s pwd=%s\n", argv[1],argv[2], argv[3]); do_auth(argv[1], argv[2], argv[3], "AS", 33); exit(0); } wstart(); while (1) { if (read_fill(0, lb, 2) != 2) exit(1); rval = get_int16(lb); if (read_fill(0, buf, rval) != rval) exit(1); switch (buf[0]) { case 'a': // auth a user pamh = NULL; user = (char *)&buf[1]; pwd = user + strlen(user) + 1; mode= pwd + strlen(pwd) + 1; sid = atoi(mode + strlen(mode) + 1); do_auth(argv[1], user, pwd, mode, sid); break; case 'c': // close session sid = atoi((char *)&buf[1]); if ((sessp = del_session(&sessions, sid)) == NULL) { fprintf(stderr, "Couldn't find session %d\r\n", sid); break; } if (sessp->session_mode == 1) { pam_close_session(sessp->pamh, 0); /*fprintf(stderr, "did ok close sess \n\r");*/ } pam_end(sessp->pamh, PAM_SUCCESS); free(sessp); break; default: fprintf(stderr, "Bad op \n\r"); } } }